如何卸载(重新加载)Python模块?

如何卸载(重新加载)Python模块?,python,module,reload,python-import,Python,Module,Reload,Python Import,我有一个长期运行的Python服务器,希望能够在不重新启动服务器的情况下升级服务。做这件事最好的方法是什么 if foo.py has changed: unimport foo <-- How do I do this? import foo myfoo = foo.Foo() 当模块已经导入时,您可以仅使用内置函数Python 3.4+重新加载模块: 在Python3中,重载被移动到模块中。在3.4中,imp被弃用为支持,并被添加到后者中。当目标为3或更高版

我有一个长期运行的Python服务器,希望能够在不重新启动服务器的情况下升级服务。做这件事最好的方法是什么

if foo.py has changed:
    unimport foo  <-- How do I do this?
    import foo
    myfoo = foo.Foo()

当模块已经导入时,您可以仅使用内置函数Python 3.4+重新加载模块:

在Python3中,重载被移动到模块中。在3.4中,imp被弃用为支持,并被添加到后者中。当目标为3或更高版本时,在调用reload或import时引用相应的模块

我想这就是你想要的。像Django的开发服务器这样的Web服务器使用它,这样您就可以看到代码更改的效果,而无需重新启动服务器进程本身

引用文件:

Python模块的代码被重新编译并 重新执行模块级代码, 定义一组新的对象,这些对象 绑定到模块的 词典函数的init函数 扩展模块不称为 第二次。与所有其他对象一样 在Python中,旧对象只是 在参考计数后收回 降到零。模块中的名称 命名空间更新为指向任何 新建或更改的对象。另外 对旧对象的引用,例如 模块外部的名称不可用 反弹以引用新对象 并且必须在每个命名空间中更新 如果需要的话,它们发生在哪里

正如您在问题中所指出的,如果Foo类驻留在Foo模块中,则必须重构Foo对象。

重新加载模块,但前提是它完全独立。如果其他任何东西都引用了模块或属于模块的任何对象,那么您将得到由旧代码比您预期的时间长而引起的微妙而奇怪的错误,以及类似isinstance的东西无法在同一代码的不同版本之间工作

如果您有单向依赖项,那么还必须重新加载依赖于重新加载的模块的所有模块,以消除对旧代码的所有引用。然后递归地重新加载依赖于重新加载的模块的模块

如果您有循环依赖关系,这在处理重新加载包时非常常见,则必须一次性卸载组中的所有模块。您不能使用reload执行此操作,因为它将在刷新依赖项之前重新导入每个模块,从而允许旧引用潜入新模块

在这种情况下,唯一的方法是攻击sys.modules,这是一种不受支持的方法。您必须检查并删除希望在下次导入时重新加载的每个sys.modules条目,还必须删除其值为“无”的条目,以处理与缓存失败的相对导入有关的实现问题。这不是很好,但只要您有一组完全自包含的依赖项,并且不将引用留在其代码库之外,它是可行的


可能最好重新启动服务器:-

如果模块不是纯Python的,那么删除它可能特别困难

以下是来自以下方面的一些信息:

您可以使用sys.getrefcount来查找 参考资料

大于3的数字表示 这将是很难摆脱的 单元土生土长的房子空空如也 不包含任何内容的模块应为 垃圾收集后

因为第三个引用是一个工件 getrefcount函数的


在Python 3.0–3.3中,您将使用:

政府有这个问题

不过,谢谢


因此,我认为您现在应该使用,尽管我不确定。

对于像我这样希望在Python解释器中运行时卸载所有模块的人:


更多信息见。

以下代码允许您实现Python 2/3兼容性:

try:
    reload
except NameError:
    # Python 3
    from imp import reload

您可以在两个版本中使用它作为重载,这使事情变得更简单。

接受的答案不处理从X导入Y的情况。此代码处理它和标准导入案例:

def importOrReload(module_name, *names):
    import sys

    if module_name in sys.modules:
        reload(sys.modules[module_name])
    else:
        __import__(module_name, fromlist=names)

    for name in names:
        globals()[name] = getattr(sys.modules[module_name], name)

# use instead of: from dfly_parser import parseMessages
importOrReload("dfly_parser", "parseMessages")

在重新加载的情况下,我们将顶级名称重新分配给新重新加载的模块中存储的值,这将更新它们。

另一种方法是在函数中导入模块。这样,当函数完成时,模块将被回收垃圾

对于Python 2,请使用内置函数:

对于Python 2和Python 3.2-3.3,请使用:

进口小商品 重加载模块 对于Python≥3.4,imp支持,因此使用:

导入导入库 importlib.reloadmodule 或:

从导入lib导入重新加载 重新加载模块 TL;博士:

蟒蛇≥ 3.4:导入lib.reloadmule Python 3.2-3.3:imp.reloadmodule
python2:reloadmodule

enthoughttraits有一个模块可以很好地实现这一点

它将重新加载任何已更改的模块,并更新正在使用它的其他模块和实例化对象。它在大多数情况下不适用于_非常_私有_)方法,并且可能会阻塞类继承,但它为我节省了大量的时间 在编写PyQt GUI或在Maya或Nuke等程序中运行的东西时,需要重新启动主机应用程序的时间。它可能在20-30%的时间里不起作用,但它仍然非常有用


Enthough的软件包不会在文件更改时重新加载文件—您必须明确地调用它—但如果您真的需要它,那么实现起来应该不会太难。

对于我来说,对于Abaqus来说,这就是它的工作方式。 假设您的文件是Class_VerticesEdges.py

sys.path.append('D:\...\My Pythons')
if 'Class_VerticesEdges' in sys.modules:  
    del sys.modules['Class_VerticesEdges']
    print 'old module Class_VerticesEdges deleted'
from Class_VerticesEdges import *
reload(sys.modules['Class_VerticesEdges'])

我在尝试重新加载升华文本中的内容时遇到了很多麻烦,但最终我可以编写此实用程序,根据升华插件.py用于重新加载模块的代码,在升华文本上重新加载模块

下面允许您从名称上带有空格的路径重新加载模块,然后在重新加载后,您可以像通常那样导入模块

def reload_module(full_module_name):
    """
        Assuming the folder `full_module_name` is a folder inside some
        folder on the python sys.path, for example, sys.path as `C:/`, and
        you are inside the folder `C:/Path With Spaces` on the file 
        `C:/Path With Spaces/main.py` and want to re-import some files on
        the folder `C:/Path With Spaces/tests`

        @param full_module_name   the relative full path to the module file
                                  you want to reload from a folder on the
                                  python `sys.path`
    """
    import imp
    import sys
    import importlib

    if full_module_name in sys.modules:
        module_object = sys.modules[full_module_name]
        module_object = imp.reload( module_object )

    else:
        importlib.import_module( full_module_name )

def run_tests():
    print( "\n\n" )
    reload_module( "Path With Spaces.tests.semantic_linefeed_unit_tests" )
    reload_module( "Path With Spaces.tests.semantic_linefeed_manual_tests" )

    from .tests import semantic_linefeed_unit_tests
    from .tests import semantic_linefeed_manual_tests

    semantic_linefeed_unit_tests.run_unit_tests()
    semantic_linefeed_manual_tests.run_manual_tests()

if __name__ == "__main__":
    run_tests()

如果您是第一次运行,这将加载模块,但是如果以后您可以再次运行方法/函数,\u测试,它将重新加载测试文件。对于升华文本Python 3.3.6,这种情况经常发生,因为它的解释器永远不会关闭,除非您重新启动升华文本,即Python 3.3解释器。

这是重新加载模块的现代方式:

from importlib import reload
如果要支持早于3.5的Python版本,请尝试以下操作:

from sys import version_info
if version_info[0] < 3:
    pass # Python 2 has built in reload
elif version_info[0] == 3 and version_info[1] <= 4:
    from imp import reload # Python 3.0 - 3.4 
else:
    from importlib import reload # Python 3.5+
要使用它,请运行reloadMODULE,将模块替换为要重新加载的模块

例如,重新加载数学将重新加载数学模块。

2018-02-01

必须提前成功导入模块foo。 从importlib导入重新加载,重新加载foo 如果您不在服务器中,但是正在开发并且需要经常重新加载模块,那么这里有一个很好的提示

首先,确保您使用的是Jupyter笔记本项目中的优秀软件。安装Jupyter后,您可以使用ipython或Jupyter控制台,或者更好的Jupyter qtconsole启动它,这将为您提供一个漂亮的彩色控制台,并在任何操作系统中完成代码

现在在shell中,键入:

%load_ext autoreload
%autoreload 2
现在,每次运行脚本时,都会重新加载模块

除此之外,还有其他:


当然,它也适用于Jupyter笔记本电脑。

其他选项。请参阅Python default importlib.reload将只重新导入作为参数传递的库。它不会重新加载lib导入的库。如果您更改了很多文件,并且有一个比较复杂的包要导入,则必须进行深度重新加载

如果已安装或已安装,则可以使用函数深度重新加载所有LIB:

from IPython.lib.deepreload import reload as dreload
dreload(foo)
如果您没有Jupyter,请在shell中使用以下命令安装它:

pip3 install jupyter

使用python 3并从importlib重新加载的用户

如果您有问题,比如模块似乎无法重新加载。。。这是因为重新编译pyc需要一些时间,最多需要60秒。我写这个提示只是想告诉您是否遇到过这种问题。

Edit Answer V2 以前的解决方案只适用于获取重置信息,但它不会更改所有引用,只会重新加载,而不会更改所需的数量。为了实际设置所有引用,我必须进入垃圾收集器,并在那里重写引用。现在它就像一个符咒

请注意,如果GC关闭,或者如果重新加载GC未监视的数据,这将不起作用。如果您不想弄乱GC,原始答案可能就足够了

新代码:

import importlib
import inspect
import gc
from enum import EnumMeta
from weakref import ref


_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
               '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
               '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
               '__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
               '__basicsize__', '__base__'}


def reset_module(module, inner_modules_also=True):
    """
    This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
    module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
    to be the reloaded-module's
    :param module: The module to reload (module reference, not the name)
    :param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
    """

    # For the case when the module is actually a package
    if inner_modules_also:
        submods = {submod for _, submod in inspect.getmembers(module)
                   if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
        for submod in submods:
            reset_module(submod, True)

    # First, log all the references before reloading (because some references may be changed by the reload operation).
    module_tree = _get_tree_references_to_reset_recursively(module, module.__name__)

    new_module = importlib.reload(module)
    _reset_item_recursively(module, module_tree, new_module)


def _update_referrers(item, new_item):
    refs = gc.get_referrers(item)

    weak_ref_item = ref(item)
    for coll in refs:
        if type(coll) == dict:
            enumerator = coll.keys()
        elif type(coll) == list:
            enumerator = range(len(coll))
        else:
            continue

        for key in enumerator:

            if weak_ref_item() is None:
                # No refs are left in the GC
                return

            if coll[key] is weak_ref_item():
                coll[key] = new_item

def _get_tree_references_to_reset_recursively(item, module_name, grayed_out_item_ids = None):
    if grayed_out_item_ids is None:
        grayed_out_item_ids = set()

    item_tree = dict()
    attr_names = set(dir(item)) - _readonly_attrs
    for sub_item_name in attr_names:

        sub_item = getattr(item, sub_item_name)
        item_tree[sub_item_name] = [sub_item, None]

        try:
            # Will work for classes and functions defined in that module.
            mod_name = sub_item.__module__
        except AttributeError:
            mod_name = None

        # If this item was defined within this module, deep-reset
        if (mod_name is None) or (mod_name != module_name) or (id(sub_item) in grayed_out_item_ids) \
                or isinstance(sub_item, EnumMeta):
            continue

        grayed_out_item_ids.add(id(sub_item))
        item_tree[sub_item_name][1] = \
            _get_tree_references_to_reset_recursively(sub_item, module_name, grayed_out_item_ids)

    return item_tree


def _reset_item_recursively(item, item_subtree, new_item):

    # Set children first so we don't lose the current references.
    if item_subtree is not None:
        for sub_item_name, (sub_item, sub_item_tree) in item_subtree.items():

            try:
                new_sub_item = getattr(new_item, sub_item_name)
            except AttributeError:
                # The item doesn't exist in the reloaded module. Ignore.
                continue

            try:
                # Set the item
                _reset_item_recursively(sub_item, sub_item_tree, new_sub_item)
            except Exception as ex:
                pass

    _update_referrers(item, new_item)
原始答案 正如@bobince的回答中所写的,如果在另一个模块中已经有对该模块的引用,特别是如果它是使用像import numpy As np这样的As关键字导入的,那么该实例将不会被覆盖

当应用需要配置模块的干净状态的测试时,这对我来说是一个很大的问题,因此我编写了一个名为reset_module的函数,它使用importlib的reload函数并递归地覆盖所有声明模块的属性。它已经用Python版本3.6进行了测试

import importlib
import inspect
from enum import EnumMeta

_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
               '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
               '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
               '__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
               '__basicsize__', '__base__'}


def reset_module(module, inner_modules_also=True):
    """
    This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
    module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
    to be the reloaded-module's
    :param module: The module to reload (module reference, not the name)
    :param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
    """

    new_module = importlib.reload(module)

    reset_items = set()

    # For the case when the module is actually a package
    if inner_modules_also:
        submods = {submod for _, submod in inspect.getmembers(module)
                   if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
        for submod in submods:
            reset_module(submod, True)

    _reset_item_recursively(module, new_module, module.__name__, reset_items)


def _reset_item_recursively(item, new_item, module_name, reset_items=None):
    if reset_items is None:
        reset_items = set()

    attr_names = set(dir(item)) - _readonly_attrs

    for sitem_name in attr_names:

        sitem = getattr(item, sitem_name)
        new_sitem = getattr(new_item, sitem_name)

        try:
            # Set the item
            setattr(item, sitem_name, new_sitem)

            try:
                # Will work for classes and functions defined in that module.
                mod_name = sitem.__module__
            except AttributeError:
                mod_name = None

            # If this item was defined within this module, deep-reset
            if (mod_name is None) or (mod_name != module_name) or (id(sitem) in reset_items) \
                    or isinstance(sitem, EnumMeta):  # Deal with enums
                continue

            reset_items.add(id(sitem))
            _reset_item_recursively(sitem, new_sitem, module_name, reset_items)
        except Exception as ex:
            raise Exception(sitem_name) from ex

注意:小心使用!例如,在定义外部使用的类的非外围模块上使用这些选项可能会导致Python中出现内部问题,例如酸洗/取消酸洗问题。

如果遇到以下错误,此答案可能会帮助您获得解决方案:


主要问题是importlib.reload只接受模块而不接受字符串。

从sys.modules中删除模块也需要删除“None”类型

方法1:

使用簿记条目删除所有依赖项的方法2:

可选,只是为了确保所有条目都已退出,如果您选择:


实际上,当您更改一个文件时,django dev服务器会重新启动。。它会重新启动服务器,而不仅仅是重新加载模块这是从哪里来的?我没有看到关于它的文档,它也没有在我的Python 3.1.3环境中运行,也没有在2.6.4中运行。没有cdleary,Django不能只使用重载:对于具有依赖关系的模块来说,重载是不够的。请参阅下面的bobince:。这以前和现在都咬过我

浪费了整整10分钟。@jedmao@JamesDraper我很确定is_changed函数只是一个你必须编写的任意函数;它不是内置的。例如,它可能会打开与您要导入的模块对应的文件,并将其与缓存版本进行区分,以查看其是否已更改。我刚刚发现,如果模块是包的一部分,您也必须将其删除:setattrpackage,empty,none这是正确的解决方案,特别是如果您有一个包含嵌套模块的包。重新加载只会重新加载最顶层的模块,并且除非首先从sys.modules中删除它,否则不会重新加载其中的任何内容。实际上,在2.6中,这似乎无法可靠地工作,因为sys.modules.values中并非所有内容都是模块。例如:>>>typesys.modules.values[1],所以如果我试图运行它所遇到的代码,我知道这不是一个实用的解决方案,只是指出了这一点。它甚至在早期的python中都不起作用——正如所写的那样。我不得不排除一些名字。当我把代码移到我的新电脑上时,我会更新这篇文章。经过一些修改:if mod和mod.\uu name\uuu!=__main\uuuu:imp.reloadmod这对我来说很好:import imp[reloadm for m in sys.modules.values if m and not in m.\uuuuu name and not imp.is\u builtinm.\uuuu name\uuu]认真的新手很高兴了解Python 2和3之间的关键细微差别。@LoïcFaure Lacroix在Python 2中重新加载内置代码的方法同样有效。x@Tarrasch:这是您要重新加载的Python模块,如问题中的示例所示。@LoïcFaure Lacroix是的,imp可以自行重新加载。@PaulD.Waite,你能确认这在Python3.6.5中是有效的吗?dreload不是专门针对那个场景的吗?@Josh:no,它是用于重新加载包树的,即使如此,它也只能在包没有外部/循环依赖项的情况下工作。您能否用无值详细说明该部分,因为我正遇到这个问题:我正在从sys.modules中删除项,重新导入后,一些导入的依赖项是无的。@shclamar:请参阅和其中的链接以获取背景信息。我甚至不清楚import.c代码在删除“真实”条目时,None条目是如何通过导入机制返回的,我似乎无法在2.7上实现这一点;在未来,这肯定不再是一个问题,因为隐性相对进口已经消失。同时,删除所有没有值的条目似乎可以解决这个问题。@Elitessaiyan:你是说重载函数吗?它是内置的,您不必导入任何库。+1。我的目标是在python中运行鼻子测试。在我加载了一个模块并重命名了一些函数之后,调用nose.run时,旧名称仍然存在,即使在重新加载my_模块%run my_模块之后,如果您的模块导入了它自己的子模块,您可能也需要删除这些子模块。类似于[delsys.modules[mod]for mod in sys.modules.keys if mod.startswith'myModule.],我认为这不会卸载模块。在Python3.8上:导入sys;导入json;del sys.modules['json'];printjson.dumps[1]和json模块仍在工作,即使它不再在sys.modules中。是的,我注意到一个带下划线的sys.modules[''''u json']条目,并且在考虑到mod in[m for m in sys.modules if m.lstrip'''''''.'startswith'json']:del sys.modules[mod]yes后,它仍然会打印出来。即使使用更激进的簿记,删除条目及其所有依赖项也不会删除模块的功能。before=[mod for mod in sys.modules];导入json;after=[mod for mod in sys.modules(如果mod不在before中));对于mod in[m for m in sys.modules if m in after]:del sys.modules[mod]代码块没有保留换行符;表示换行备忘提示:导入并不意味着加载,它意味着如果尚未加载则加载,然后导入到命名空间中。问题不应包括“卸载”,因为这在python中尚不可能实现-不过,重载是一个已知的范例,如下所述。在py2exe应用程序中使用动态模块时,WI也有相同的问题。由于py2exe始终将字节码保留在zip目录中,因此重新加载不起作用。但我发现了一个使用import_文件模块的有效解决方案。现在,我的应用程序运行正常。如果您想卸载,因为代码正在使用试图删除.pyc文件,该怎么办?注意到一个问题,globals指的是您在其中定义此函数的模块,因此如果您在与您在中调用它的模块不同的模块中定义它,则此模块不起作用。对于interactive,在>>>从X导入Y到重新加载后执行>>>\uu导入'X',fromlist='Y'@BobStein VisiBone,当fromlist='*'时,有没有办法使其工作?好问题,不知道@MikeC。顺便说一句,我倾向于在import语句中几乎停止使用from。只需在代码中导入和显式package.symbol。认识到这可能并不总是可能或可取的。这里有一个例外:来自未来的import print_函数;从foo import*这个答案是这里的直接副本:模块将永远不会
r获取垃圾回收,因为全局引用至少保存在sys.modules中。若要处理以下任何情况:从六个导入重新加载\u模块需要先安装六个course@Anentropic:建议使用six软件包是一个很好的建议,但语法来自six.moves import reload_模块或仅从importlib import reload执行。然后您可以重新加载模块名称。没有必要使用此函数。我认为modulereloadMODULE_名称比重新加载Module_名称更容易解释,并且与其他函数冲突的可能性更低。@RichieBendall抱歉,但这个答案完全错误。重载函数接受模块对象,而不是模块名称。。。阅读文档:我同意@pault-这是因为ModuleLoad是超级棒。我已经更改了我的答案以反映您的意见。这两个Ipython dreload和从importlib重新加载都抱怨重新加载参数必须是模块。我正在使用自定义函数导入,但似乎不起作用。使用内置模块确实有效。:-我对代码所做的每一个小改动都是在浪费时间重新加载iPython。。。
from sys import version_info
if version_info[0] < 3:
    pass # Python 2 has built in reload
elif version_info[0] == 3 and version_info[1] <= 4:
    from imp import reload # Python 3.0 - 3.4 
else:
    from importlib import reload # Python 3.5+
%load_ext autoreload
%autoreload 2
%autoreload
Reload all modules (except those excluded by %aimport) automatically now.

%autoreload 0
Disable automatic reloading.

%autoreload 1
Reload all modules imported with %aimport every time before executing the Python code typed.

%autoreload 2
Reload all modules (except those excluded by %aimport) every time before
executing the Python code typed.
from IPython.lib.deepreload import reload as dreload
dreload(foo)
pip3 install jupyter
import importlib
import inspect
import gc
from enum import EnumMeta
from weakref import ref


_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
               '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
               '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
               '__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
               '__basicsize__', '__base__'}


def reset_module(module, inner_modules_also=True):
    """
    This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
    module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
    to be the reloaded-module's
    :param module: The module to reload (module reference, not the name)
    :param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
    """

    # For the case when the module is actually a package
    if inner_modules_also:
        submods = {submod for _, submod in inspect.getmembers(module)
                   if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
        for submod in submods:
            reset_module(submod, True)

    # First, log all the references before reloading (because some references may be changed by the reload operation).
    module_tree = _get_tree_references_to_reset_recursively(module, module.__name__)

    new_module = importlib.reload(module)
    _reset_item_recursively(module, module_tree, new_module)


def _update_referrers(item, new_item):
    refs = gc.get_referrers(item)

    weak_ref_item = ref(item)
    for coll in refs:
        if type(coll) == dict:
            enumerator = coll.keys()
        elif type(coll) == list:
            enumerator = range(len(coll))
        else:
            continue

        for key in enumerator:

            if weak_ref_item() is None:
                # No refs are left in the GC
                return

            if coll[key] is weak_ref_item():
                coll[key] = new_item

def _get_tree_references_to_reset_recursively(item, module_name, grayed_out_item_ids = None):
    if grayed_out_item_ids is None:
        grayed_out_item_ids = set()

    item_tree = dict()
    attr_names = set(dir(item)) - _readonly_attrs
    for sub_item_name in attr_names:

        sub_item = getattr(item, sub_item_name)
        item_tree[sub_item_name] = [sub_item, None]

        try:
            # Will work for classes and functions defined in that module.
            mod_name = sub_item.__module__
        except AttributeError:
            mod_name = None

        # If this item was defined within this module, deep-reset
        if (mod_name is None) or (mod_name != module_name) or (id(sub_item) in grayed_out_item_ids) \
                or isinstance(sub_item, EnumMeta):
            continue

        grayed_out_item_ids.add(id(sub_item))
        item_tree[sub_item_name][1] = \
            _get_tree_references_to_reset_recursively(sub_item, module_name, grayed_out_item_ids)

    return item_tree


def _reset_item_recursively(item, item_subtree, new_item):

    # Set children first so we don't lose the current references.
    if item_subtree is not None:
        for sub_item_name, (sub_item, sub_item_tree) in item_subtree.items():

            try:
                new_sub_item = getattr(new_item, sub_item_name)
            except AttributeError:
                # The item doesn't exist in the reloaded module. Ignore.
                continue

            try:
                # Set the item
                _reset_item_recursively(sub_item, sub_item_tree, new_sub_item)
            except Exception as ex:
                pass

    _update_referrers(item, new_item)
import importlib
import inspect
from enum import EnumMeta

_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
               '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
               '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
               '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
               '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
               '__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
               '__basicsize__', '__base__'}


def reset_module(module, inner_modules_also=True):
    """
    This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
    module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
    to be the reloaded-module's
    :param module: The module to reload (module reference, not the name)
    :param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
    """

    new_module = importlib.reload(module)

    reset_items = set()

    # For the case when the module is actually a package
    if inner_modules_also:
        submods = {submod for _, submod in inspect.getmembers(module)
                   if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
        for submod in submods:
            reset_module(submod, True)

    _reset_item_recursively(module, new_module, module.__name__, reset_items)


def _reset_item_recursively(item, new_item, module_name, reset_items=None):
    if reset_items is None:
        reset_items = set()

    attr_names = set(dir(item)) - _readonly_attrs

    for sitem_name in attr_names:

        sitem = getattr(item, sitem_name)
        new_sitem = getattr(new_item, sitem_name)

        try:
            # Set the item
            setattr(item, sitem_name, new_sitem)

            try:
                # Will work for classes and functions defined in that module.
                mod_name = sitem.__module__
            except AttributeError:
                mod_name = None

            # If this item was defined within this module, deep-reset
            if (mod_name is None) or (mod_name != module_name) or (id(sitem) in reset_items) \
                    or isinstance(sitem, EnumMeta):  # Deal with enums
                continue

            reset_items.add(id(sitem))
            _reset_item_recursively(sitem, new_sitem, module_name, reset_items)
        except Exception as ex:
            raise Exception(sitem_name) from ex
Traceback (most recent call last): File "FFFF", line 1, in NameError: name 'YYYY' is not defined Traceback (most recent call last): File "FFFF", line 1, in File "/usr/local/lib/python3.7/importlib/__init__.py", line 140, in reload raise TypeError("reload() argument must be a module") TypeError: reload() argument must be a module
import sys
import json  ##  your module

for mod in [ m for m in sys.modules if m.lstrip('_').startswith('json') or sys.modules[m] == None ]: del sys.modules[mod]

print( json.dumps( [1] ) )  ##  test if functionality has been removed
import sys

before_import = [mod for mod in sys.modules]
import json  ##  your module
after_import = [mod for mod in sys.modules if mod not in before_import]

for mod in [m for m in sys.modules if m in after_import or sys.modules[m] == None]: del sys.modules[mod]

print( json.dumps( [2] ) )  ##  test if functionality has been removed
import gc
gc.collect()