有没有办法使用 Python RQ 从 __main__ 提交函数

she*_*idp 4 python python-rq

此问题类似,有什么方法可以将同一文件中定义的函数提交给 python-rq?@GG_Python 谁让我为此创建一个新问题。

用法示例:

# somemodule.py
from redis import Redis
from rq import Queue

def somefunc():
    do_something()

q = Queue(connection=Redis('redis://redis'))
q.enqueue(somefunc)
Run Code Online (Sandbox Code Playgroud)

是的,我知道答案是在 someothermodule.py 中定义 somefunc ,然后在上面的代码段中定义from someothermodule import somefunc,但我真的不想。也许我对表单过于执着,但 somefunc 确实属于它排队的同一个文件(实际上, somefunc 接受一个 docker 容器名称并生成它)。我真的希望整个事情都是自包含的,而不是有两个模块。

我注意到,通过挖掘 python-rq 源代码,Queue.enqueue 实际上可以接受一个字符串而不是实际的模块,所以我希望我可以通过somemodule.somefunc,但没有那么幸运。有任何想法吗?

kin*_*all 9

查看源代码, rq 只是检查您的函数的__module__属性,可以轻松更改。问题是,为什么rq 限制您从 将作业排入队列__main__?一定有某种原因,并且有:函数的模块必须可由工作人员导入。__main__不是,因为您的主模块未__main__.py在磁盘上命名。请参阅本页底部的“工作注意事项” 。

此外,您的脚本中包含顶级(非定义)代码,每次由工作人员导入时都会重新调用,您可能不想这样做,因为它会创建新队列并填充它们每个工人开始时的工作,无限。如果你想在你的主模块中加入一个函数,你可以并且应该用一个if __name__ == "__main__"守卫来防止这种递归行为。

如果您想将函数及其队列保留在单个模块中,我的建议是除了函数和/或类定义之外,不要在其中放置任何顶级代码。任何将是顶级代码的东西,都写成一个函数(命名为 eg main())。然后编写一个“虚拟”主模块,导入您的真实模块并开始处理。

例子:

某个模块.py
from redis import Redis
from rq import Queue

def somefunc():
    do_something()

def main():
    q = Queue(connection=Redis('redis://redis'))
    q.enqueue(somefunc)

# if the user tried to execute this module, import the right one for them.
# somemodule is then imported twice, once as __main__ and once as somemodule,
# which will use a little extra memory and time but is mostly harmless
if __name__ == "__main__":
    import mainmodule
Run Code Online (Sandbox Code Playgroud) 主模块.py
import somemodule
somemodule.main()
Run Code Online (Sandbox Code Playgroud)

您也可以__module__将函数的属性更改为磁盘上模块的实际名称,以便可以导入。您甚至可以编写一个装饰器来自动执行此操作:

from sys import modules
from from os.path import basename, splitext

def enqueueable(func):
    if func.__module__ == "__main__":
        func.__module__, _ = splitext(basename(modules["__main__"].__file__))
    return func

@enqueueable
def somefunc():
    do_something()

if __name__ == "__main__":
    from redis import Redis
    from rq import Queue

    q = Queue(connection=Redis('redis://redis'))
    q.enqueue(somefunc)
Run Code Online (Sandbox Code Playgroud)

为简单起见,装饰器假定您的模块是一个可在其文件名下导入且.py去掉扩展名的单个文件。您可能正在为您的主模块使用一个包,其中的事情会变得更加复杂......所以不要这样做。