如何给__import__动态引入的文件添加库(在线等,解决 V 币感谢) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
akmonde
V2EX    Python

如何给__import__动态引入的文件添加库(在线等,解决 V 币感谢)

  •  
  •   akmonde 2018-08-28 15:51:38 +08:00 2979 次点击
    这是一个创建于 2667 天前的主题,其中的信息可能已经有所发展或是发生改变。

    现在我这边有个问题,由于存在多个不规则插件,所以在入口文件处,通过__import__加载路径对多个插件进行引用。

    不过由于某些原因,调用插件时,插件并没有能继承入口文件中已经引入的库(包括公共库和 diy 的第三方库)。

    所以现在需要想法子,需要给插件动态引入库,不然各个插件会因为缺失库报错。这里实在不想人工给每个插件都 import 一遍一坨库,法子有点蠢而且不实用。

    解决尝试:

    本来想试试 setattr 啥的,不过好像只能赋值,不能引用库。 又尝试给多个子级目录添加了__init__.py ,再在里面 import 库,也没有生效。 

    请问大佬们有没有相应的解决方案?

    第 1 条附言    2018-08-28 16:37:24 +08:00
    @owenliang

    刚才讲的“某些原因”如下:

    celery 创建 subtask,然后再在 subtask 里面引入插件,插件居然不能继承入口文件里面引入的库文件,我也很无奈。

    本来起先以为是路径不对,后来入口文件直接引用原生库,再调用插件后插件里也没继承,引用不了该库。

    问题出的有点莫名其妙,所以我觉得没啥人会关心这个。
    28 条回复    2018-08-31 12:10:26 +08:00
    wwqgtxx
        1
    wwqgtxx  
       2018-08-28 16:29:47 +08:00
    有个不是很优雅的实现方法,假设你的插件在 /plugin 目录下,你在程序启动时创建(如果已经存在则删除后重建)一个 /_plugin 目录,然后把源 plugin 目录下的文件一个个的拷贝过去,在拷贝的时候,只要是.py 文件就在开头追加一段你需要的"import xxx;import xxx",最后再用__import__("._plugin/xxxxx")
    owenliang
        2
    owenliang  
       2018-08-28 16:30:07 +08:00
    某些原因到底是啥原因
    akmonde
        3
    akmonde  
    OP/div>
       2018-08-28 16:39:28 +08:00
    @wwqgtxx 以前这么做过,感觉有点傻..这边有几千个文件,每个都加,就算是批量也感觉有点难看。
    owenliang
        4
    owenliang  
       2018-08-28 17:12:01 +08:00
    main.py


    # -*- coding: utf-8 -*-

    import sys
    import time

    plugin_util = __import__("plugins.util", globals(), locals(), 'util')


    plugins/util.py

    import sys

    g_vars = globals()

    for name in sys.modules:
    g_vars[name] = sys.modules[name]

    print(time.clock())

    你是指这个意思吗?把 sys 中的 modules 展开到 plugin 的全局变量里?
    xuboying
        5
    xuboying  
       2018-08-28 17:15:56 +08:00
    akmonde
        6
    akmonde  
    OP
       2018-08-28 17:30:45 +08:00
    @owenliang 感谢回复,另外简单表述下,比如存在 plugins/util.py 文件,本来这文件可能本身没有 import sys 或者其他第三方库,我想办法需要在 main.py 里面,把 import sys,赋给 plugins/util.py [可能有很多 plugins,都需要赋给] ,然后保证在调用 plugins/util.py 时候不会出错。
    owenliang
        7
    owenliang  
       2018-08-28 17:42:00 +08:00   1
    yufpga
        8
    yufpga  
       2018-08-28 18:13:16 +08:00
    引用六楼的回复,依据![python 包导入机制]( https://blog.csdn.net/tz_zs/article/details/77018298) 你这样子应该是行不通的。如果可以这样做,python 的包导入将毫无安全性可言。我觉得应该从代码层面去彻底避免这个问题。
    baojiweicn2
        9
    baojiweicn2  
       2018-08-28 18:27:17 +08:00 via iPhone
    importlib 了解下
    akmonde
        10
    akmonde  
    OP
       2018-08-28 19:44:40 +08:00
    @owenliang 还是非常感谢,不过我这边 plugin 太多了,都去注册一下某个库文件不太现实。
    akmonde
        11
    akmonde  
    OP
       2018-08-28 19:56:19 +08:00
    @baojiweicn2
    @xuboying
    两位的意思是 importlib 有个设置 globals 和 locals 的选项么?如果是的话可能有点尴尬,这特性好像是 3.x 引入的,我这边是 2.7,似乎没有这两个扩展选项。
    见:
    ```
    https://docs.python.org/2.7/library/importlib.html
    ```
    skinny
        12
    skinny  
       2018-08-28 20:53:19 +08:00
    pkgutil + importlib

    具体使用可以参考 scrapy 的以下两个模块:
    walk_modules https://github.com/scrapy/scrapy/blob/master/scrapy/utils/misc.py
    iter_spider_classes https://github.com/scrapy/scrapy/blob/master/scrapy/utils/spider.py
    akmonde
        13
    akmonde  
    OP
       2018-08-28 22:52:56 +08:00
    @skinny 谢谢指教,不过看起来好像问题复杂化了,貌似是换了两种 import 方式么。
    弱弱问句,这个能让我这边调用的插件,继承我入口文件的已经加载的库么...
    主要没看到特别的说明和参数表示能做到这点...
    firejoke
        14
    firejoke  
       2018-08-28 23:05:46 +08:00
    之前我有一个需求是希望可以运行时指定映射的 model
    我是 import_module 模块做的:

    def migrate(model_path: str = None):
    """
    定义一个实现 orm 映射 model 到 DB 的方法
    因为在 commit 之前,所有的表创建与操作实际上是在内存里
    试着用实例化某个模型的方式来实现自由映射模型
    避免用 create_all()来映射所有继承 Base 的模型
    但不行
    所以尝试动态导入模型来自由映射
    main_dir/
    test/
    models
    import_module("test.models")
    """
    try:
    # 动态导入要映射的模型
    import_module(model_path + "." + "models" if model_path else "models")
    # 把表创建进内存
    Base.metadata.create_all()
    # 把内存里的表写进数据库
    db_session.commit()

    except (ImportError, TypeError) as e:
    print(e)
    db_session.rollback()
    firejoke
        15
    firejoke  
       2018-08-28 23:08:48 +08:00
    wwqgtxx
        16
    wwqgtxx  
       2018-08-28 23:48:32 +08:00   1
    还有个比较暴力的办法
    import sys,imp

    f = open("plugin/xxx.py",'r')
    code = "import A \n import B \n" + f.read()
    f.close()
    module = imp.new_module('plugin.xxx')
    exec code in mymodule.__dict__
    sys.module['plugin.xxx'] = module

    如果是 python3 的话应该这样
    from types import ModuleType
    import sys

    f = open("plugin/xxx.py",'r')
    code = "import A \n import B \n" + f.read()
    f.close()
    mod = ModuleType('plugin.xxx', '')
    exec(code, mod.__dict__)
    sys.module['plugin.xxx'] = module
    wwqgtxx
        17
    wwqgtxx  
       2018-08-28 23:50:13 +08:00
    但是上述方法在出现 plugin 之间的相互引用的时候还是容易导致错误,这个需要用 import_hook 进一步优化了
    akmonde
        18
    akmonde  
    OP
       2018-08-29 00:21:26 +08:00
    @yufpga 明儿再看看,我白天就是没有找到问题根源,也就是关于 celery subtask 调用插件带来的库不继承问题。
    @wwqgtxx 这法子估计能行,不过插件多了以后估计会比较慢,我明儿看看。实在不行只能批量加在插件头部,或者这样弄了。
    @firejoke 兄 dei,讲真你这写的不错的。不过好像跟我需求不太一样啊。我不是只找动态映射啊,是库继承不了的问题。
    skinny
        19
    skinny  
       2018-08-29 07:56:42 +08:00
    @akmonde
    主要的流程很简单:
    一,是从特定模块路径递归导入模块,并将返回导入的模块列表。walk_modules 就使用了 importlib.import_module 来导入,和 pkgutil.iter_modules 来搜索子模块(自 Python 3.3 开始直接基于 importlib 实现的),没别的复杂设计。
    二,就是从一导入的模块列表里寻找特定类,比如找到特定类的子类什么的,这个你可以自己决定怎么写(比如检测模块中有某个名字的方法)。

    建议 plugin 的基类和 plugin 实现之类的放在不同目录,避免使用如上方法自动搜索和导入的时候出现重复操作。

    这个和你已经加载的插件并不冲突,不过你可以在流程一或二阶段跳过已经加载的模块(注册下已加载的模块名字呗)。
    akmonde
        20
    akmonde  
    OP
       2018-08-29 09:15:25 +08:00
    @skinny emmmm,原谅我有点笨,我看过那两段代码,您也写的蛮清晰的。不过这个为啥能让我那边入口函数的库能够被子模块继承,这点我没太懂...
    wwqgtxx
        21
    wwqgtxx  
       2018-08-29 10:59:44 +08:00
    @skinny 说实话,我觉得你的实现方法并不能解决题主在 3#描述的问题,你的方案只是解决了批量导入模块的问题,却没办法在导入模块前动态给模块的开头加上 import
    wwqgtxx
        22
    wwqgtxx  
       2018-08-29 11:04:30 +08:00
    应该是 6#刚才打错了
    skinny
        23
    skinny  
       2018-08-29 14:38:41 +08:00
    @wwqgtxx 确实没办法。实际上我也没有找到 Python 模块注入 100%可行的办法,确切的说没有找到正常的在 import 前设置符号表的方法。如果他的插件模块在模块域就使用了未导入的模块名字,那目前我没有找到办法,如果是在类或函数时还好,批量导入时对那个模块设置下模块名字就好了。

    伪代码:
    def walk_modules(path):
    .... ....
    .... mod = import_module(path)
    .... mod.sys = sys # or import_module('sys')
    .... ....

    如果不愿意重构,就写个脚本批量处理下那些插件代码呗。
    wwqgtxx
        24
    wwqgtxx  
       2018-08-29 16:23:31 +08:00   1
    @skinny 基本上除了我在#16 提供的使用 exec 的方法以外,想要动态重构一个模块还真的很难。你提供的方法只要模块能正常的被 import 就能把其他模块比如 sys 注入,但是如果在 import 的时候就直接报错 ImportError 之类的就无能为力了。而我提供的方法其实是完全自己手动模拟的 import 的过程,但是无法解决出现一个插件内部 import 另一个插件的情况(当然也可以在 read 之后把文件内部的 import 再 hook 一下,不过这样就非常的臃肿了)。
    看楼主的描述,其实是那些个插件本身写的就有语法问题
    至于楼主说的“插件居然不能继承入口文件里面引入的库文件”,其实这是个很合理的行为,每个.py 文件都有一个自己独立的作用域,换句话说,除非手动 import 否则各个模块之间应该互相不干扰,贝莱就没有所谓的入口文件的概念,自然也就不存在继承的问题
    skinny
        25
    skinny  
       2018-08-29 17:19:51 +08:00   1
    @wwqgtxx 你都读入模块源代码拼接了 import 语句了,还不如写个脚本一次性处理下那些脚本。说真的,按描述看,楼主那些插件代码一塌糊涂,得重写才能避免那些问题。
    akmonde
        27
    akmonde  
    OP
       2018-08-29 22:44:04 +08:00
    @skinny
    @wwqgtxx
    讲真那些插件代码应该没问题,github 上搜罗的,不算是我的锅...
    我想了想,可能是因为使用了 celery 的 subtask 调用的插件,才造成不能共享库的作用域,直接调用插件的话库本来可以共享作用域的。

    @baojiweicn2 emmmm,查了下,好像这条是 celery 任务自动运行所有已注册的 app 吧。我那边呢,是需要动态导入一批未知名的模块,然后再将它们注册成 celery app 再运行,估计这法子可能不大合适。
    akmonde
        28
    akmonde  
    OP
       2018-08-31 12:10:26 +08:00
    @wwqgtxx
    @skinny
    最后是了把需要包含的,一的文件,然後再直接在所有插件的部插入 import....真香,居然是用了老法...
    最後遇到坑,workflow 的 chord 等等,win 下不支持的( celery v3.x ),排花了老半天。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5168 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 05:59 PVG 13:59 LAX 21:59 JFK 00:59
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86