Python共享对象模块命名约定

23

我用C++编写了一个Python模块并将其构建为共享对象库,它能够正常工作。但是在弄清楚所有这些之后,我注意到(通过strace),Python寻找几种不同的import调用方式。特别是当我使用import foo时,Python按顺序搜索以下内容:

  • foo(目录)
  • foo.so
  • foomodule.so
  • foo.py
  • foo.pyc

这一切都很好理解,除了 foomodule.so。为什么Python会同时寻找name.so和namemodule.so?这是历史遗留问题吗?我搜索了很多,没有找到任何解释,所以我不确定是否应该将我的模块命名为foomodule.so而不是foo.so。我的系统似乎已经存在一些遵循每种约定的Python模块,因此我不禁想知道不同的名称是否意味着什么。

2个回答

21
这实际上是与平台相关的,Python会根据操作系统尝试不同的后缀。以下是在import.c中初始化后缀表的代码:
#ifdef HAVE_DYNAMIC_LOADING
    memcpy(filetab, _PyImport_DynLoadFiletab,
           countD * sizeof(struct filedescr));
#endif
    memcpy(filetab + countD, _PyImport_StandardFiletab,
           countS * sizeof(struct filedescr));
    filetab[countD + countS].suffix = NULL;

    _PyImport_Filetab = filetab;

所以它连接了两个列表,_PyImport_DynLoadFiletab_PyImport_StandardFiletab。后者更容易,它在同一文件中被定义为[".py", ".pyw", ".pyc"](第二个条目仅在Windows上存在)。_PyImport_DynLoadFiletab 在各种dynload_<platform>.c文件中定义。在基于Unix的系统中,其值为[".so", "module.so"],对于CygWin,它定义了[".dll", "module.dll"],而对于OS/2,它是[".pyd", ".dll"],对于Windows,它只是[".pyd"]

我查看了源代码历史记录,最终找到了这个1999年的更改,显然添加了"module.so"作为可能的后缀名: http://hg.python.org/cpython-fullhistory/diff/8efa37a770c6/Python/importdl.c。因此,这些更改最初是为NeXTStep(最终成为Mac OS X)添加的,仅适用于特定的链接设置。我不了解这个操作系统,所以很难说为什么要这样做 - 我怀疑这只是为了防止命名冲突。例如,一个框架库foo.so可能已经被加载,操作系统不允许加载另一个同名的库。因此,foomodule.so是一种妥协,允许存在一个名为foo的Python模块。

编辑: 上面的段落是错误的 - 我没有回溯到足够早的历史,感谢senderle指出这一点。实际上,有趣的变化似乎出现在1994年的http://hg.python.org/cpython-fullhistory/diff/2230/Python/import.c,那时添加了一个新的模块命名方案(foo.so)作为旧方案(foomodule.so)的替代方案。我猜旧形式在某个时候变得不推荐使用,因为像Windows这样的一些平台已经删除了对它的支持,在该代码的众多重写之一中。请注意,即使在最初引入时,短模块名称版本也被列在首位,这意味着它已经是首选变体。

编辑2: 我搜索了1994年的邮件列表/新闻组,看看是否在某个地方讨论了这个变化 - 看起来没有,Guido van Rossum似乎在没有告诉任何人的情况下实施了它。


有趣,我不知道那个完整历史记录浏览器。里面有一些深刻的机构记忆。然而,如果你指的是 #define LONG_EXT "module.so" 的定义,那么它在你谈论的补丁之前很久就存在了。事实上,在 importdl.c 创建时就已经存在了! - senderle
@senderle:谢谢,我确实犯了一个错误。importdl.c是在重写中创建的,原始代码可以在import.c中找到 - 我找到了引入两种命名方案的更改并更新了我的答案。很遗憾,Python机构的记忆不包括导致变化的讨论。 - Wladimir Palant

10

这只是一个猜测,但我只能假设这与下面的内容有关,来自于使用C或C++扩展Python

首先创建一个名为spammodule.c的文件。(从历史上看,如果模块名为spam,则包含其实现的C文件称为spammodule.c;如果模块名很长,比如spammify,则模块名可以只是spammify.c.)

我认为这个约定也适用于.so文件的名称。这个猜测在同一篇文章的1.5节中得到了进一步的支持。


基于 Wladimir 的很好的发现,我找到了module.so作为后缀的第一个引用。它来自于支持动态加载 SunOS 库的一个补丁,由“Bill.”(Bill Jansson?) 提供。 显然,在使用.so共享库之前,module作为后缀的惯例已经开始,当采用.so库时,只是简单地保持了这个惯例。

不过,我认为 Wladimir 是对的--有趣的变化在于短模块名称惯例的采用。 这证实了我的猜测,即较长的模块名称是早期的惯例。


1
我回去检查了Python寻找各种文件的顺序。我注意到它会先寻找foo.so,然后是foomodule.so,如果foomodule.so是首选形式,这似乎很奇怪。我会等待其他人的意见,因为虽然我认为你的答案合理,但它也有点含糊其辞(无意冒犯,我想我还在寻找某些来源来解释“我们为什么有两种模式”或类似的东西)。 - John Zwinck
是的,毫无疑问这有些含糊不清 :). 但我认为很明显早期存在一种不再被推荐的 foomodule 约定,但由于某些原因难以消除。我也很想听听老一辈对这个问题的看法。 - senderle
即使在添加了“module.so”之前,Python已经加载了以“module.o”文件名后缀为结尾的模块-您链接的更改仅仅添加了对SunOS的支持。 添加动态加载支持的补丁是http://hg.python.org/cpython-fullhistory/diff/609/Python/import.c,它已经使用“module.o”作为后缀-我猜这遵循了阿米巴的命名惯例。 - Wladimir Palant
@Wladimir,没错,这就是我为什么说“.so共享库”而不是一般的“共享库”的原因。此外,我非常确定在任何形式的动态加载采用之前,module.o都是传统的后缀。我想我们可以通过查看makefile来确认这一点! - senderle

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