使用python -m模块来调用一个C语言实现的模块。

17

我有一个专门为Python编写的纯C模块,我想通过使用“python -m 模块名”方法来调用它。对于用Python实现的模块,这种方法可以正常工作,其中一个明显的解决方法是添加一个额外的文件来完成这个目的。然而,我真的希望将所有内容都保留在一个单独的分发二进制文件中,而不是为此添加第二个文件。

我不在乎解决方案有多么巧妙。

如果您尝试使用-m选项加载一个C模块,则会收到错误消息No code object available for <modulename>


你使用哪些工具,想要实现什么目标,平台是什么?你使用多少标准库?这个模块是你自己的闭源代码,还是我们可以查看的开源代码?没有一些信息我无法提供帮助。 - janislaw
感谢您的热情,但问题比您的问题所示的要深得多。该模块是使用C编写的,使用标准Python方法进行编写。请从此页面开始了解:http://docs.python.org/extending/extending.html。“工具”是用于C扩展的标准Python机制,我正在尝试实现的内容在描述中,所有平台,标准库不相关(代码不是Python而是C),该模块是我的并且是开源的,足够复杂,建议使用来自python文档页面的示例模块。 - Roger Binns
你是否曾经在Python中生成过冻结的可执行文件?根据平台的不同,它会捆绑所有需要运行的可执行代码。我曾经将一个Python程序移植到Linux上,并不得不随着冻结的可执行文件一起发布libglib和libz。另一方面,在Windows上,你可能还需要发布msvcrtxx.dll。即使你使用Python.lib静态链接编写C程序,你仍然需要附加动态库,这违背了单文件的目的。 - janislaw
2
是的,适用于Windows,Linux和Mac。这完全不同。我的模块是普通模块,可以像其他模块一样使用。它还包括一个shell。目前,要获取shell,您必须执行“python -c 'import module;module.main()'”,而我希望它变成“python -m module”。 - Roger Binns
1
你是否有明显的理由更喜欢将一个(可能是)预编译的共享库分发给用户自行安装,而不是使用distutils或类似的工具?我几乎不会安装任何Python模块,无论多么有用,除非可以使用pip或easy_install进行安装。 - SingleNegationElimination
它可以通过常规的C源代码distutils进行编译,并且除了作为单个文件更加清洁之外,没有其他的别有用心。 - Roger Binns
4个回答

7

-m 实现在 runpy._run_module_as_main 中。其本质是:

mod_name, loader, code, fname = _get_module_details(mod_name)
<...>
exec code in run_globals

编译模块没有与之关联的“代码对象”,因此第一条语句会出现错误ImportError("No code object available for <module>")。您需要扩展runpy - 具体来说是_get_module_details - 使其适用于编译模块。建议返回一个由上述"import mod; mod.main()"构造的代码对象: (Python 2.6.1)

    code = loader.get_code(mod_name)
    if code is None:
+       if loader.etc[2]==imp.C_EXTENSION:
+           code=compile("import %(mod)s; %(mod)s.main()"%{'mod':mod_name},"<extension loader wrapper>","exec")
+       else:
+           raise ImportError("No code object available for %s" % mod_name)
-       raise ImportError("No code object available for %s" % mod_name)
    filename = _get_filename(loader, mod_name)

(更新:修正格式字符串中的一个错误)
现在...
C:\Documents and Settings\Пользователь>python -m pythoncom

C:\Documents and Settings\Пользователь>

对于内置模块,这仍然不起作用。同样,您需要为它们发明一些“主代码单元”的概念。

更新:

我已经查看了从 _get_module_details 调用的内部函数,并且可以自信地说,它们甚至不会尝试从类型为 imp.PY_SOURCEimp.PY_COMPILEDimp.PKG_DIRECTORY 的模块中检索代码对象。因此,您必须以这种或那种方式修补此机制,以使 -m 起作用。Python 在从您的模块检索任何内容之前就失败了(它甚至不检查 dll 是否为有效模块),因此您无法通过特殊方式构建来实现任何东西。


感谢您提供详细的答案。我希望我能在我的C代码中实现一些特殊操作,以返回附加代码对象的模块。 - Roger Binns
从管理的角度来看,一个包装脚本对我来说似乎是最好的解决方案。很容易给它命名和放置,以便在用户调用“python”时调用它而不是python.exe。 - ivan_pozdeev
除了"python -m module"之外的任何东西都存在问题,因为我必须要分发多个文件,然后用户还需要弄清楚把第二个文件放在哪里并且能够执行它。考虑不同的权限(用户与管理员)、不同的平台、对于我的模块开发人员和用户的区别等等。令人遗憾的是,Python不允许-m与C扩展一起使用,甚至没有一个可行的恶心解决方法。 - Roger Binns
2
这个答案已经创建了将近8年了。有人知道我是否仍然需要制定解决方案来使用“-m”标志执行cythonized脚本吗?例如:python -m app.module - HereHere

0

你的单一分布式二进制需求是否允许使用egg?如果是这样,你可以将你的模块与一个包含调用代码和通常的__init__.py的__main__.py打包...

如果你非常坚持,也许你可以扩展pkgutil.ImpLoader.get_code以返回C模块的内容(例如,可能是一个特殊的__code__函数)。为了做到这一点,我认为你必须实际上在Python源代码中进行更改。即使如此,pkgutil仍然使用exec来执行代码块,因此它仍然需要Python代码。

简而言之:我认为你被卡住了。虽然Python模块在导入时具有在全局级别运行的代码,但C模块没有;它们大多只是一个字典命名空间。因此,从概念上讲,运行C模块并没有太多意义。你需要一些真正的Python代码来指导操作。


我不相信 eggs 支持编译后的代码。因为那是代码运行最早的时刻,所以我只能在我的模块加载时扩展 pkgutil,但那已经太晚了。目前的解决方案真的很令人恼火,要么使用 python -c "import module;module.main()" 或者创建一个只包含这 4 个单词的 .py 文件。 - Roger Binns

-2

我认为你需要从在Python中创建一个单独的文件开始,然后让-m选项起作用。然后将这个Python文件转换成一个代码对象,并以这样一种方式将它合并到你的二进制代码中,使得它继续工作。

在PyPi中查找setuptools,下载.egg文件并查看文件。你会发现前面几个字节包含了一个Python脚本,这些脚本后面跟着一个.ZIP文件字节流。类似的方法也可能适用于你。


1
获取Python字节码非常简单 - Py_CompileString可以实现。然而,我看不到将字节码附加到从Py_InitModule3/PyModule_Create返回的PyObject的方法。 - Roger Binns

-2

1
不适用于远程。我希望我的模块能够与已安装的Python一起工作,而不是单独构建一个。 - Roger Binns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接