Python 2.4中标准库覆盖了Python导入的问题

6
我正在编写一个Python包,但由于命名冲突的问题,标准库被导入而不是我的文件。
例如,以下是一个文件结构:

package/__init__.py

# No data in this file

package/module.py

#!/usr/bin/env python
print 'Loading module.py'
import signal

package/signal.py

#!/usr/bin/env python
print 'Loading signal.py'

当我运行这个时,会得到以下结果:
$ ./module.py
Loading module.py

我想要获取:

$ ./module.py
Loading module.py
Loading signal.py

实际问题:

当我运行 module.py 时,它的 import signal 引用了标准库版本。我如何强制 module.py 导入 signal.py 呢?

正如标签中所述,这需要在 python-2.4.3 上运行。虽然这是一个旧版本,但它包含在 RHEL 5 中。


一些额外信息

只是为了提供更多信息,我明确地设置了以下内容:

[10:30pm][~/test] tree .
.
|-- package
|   |-- __init__.py
|   |-- module.py
|   `-- signal.py
`-- script

[10:30pm][~/test] cat script
#!/usr/bin/env python
from package import signal

[10:30pm][~/test] cat package/__init__.py

[10:30pm][~/test] cat package/module.py 
#!/usr/bin/env python
print "Loading module.py"
import signal

[10:30pm][~/test] cat package/signal.py 
#!/usr/bin/env python
print "Loading signal.py"

[10:30pm][~/test] python ./script
Loading signal.py

[10:32pm][~/test] python ./package/module.py 
Loading module.py

[10:32pm][~/test] python -m package.module
python: module package.module not found

请注意,当我运行 ./package/module.py 时,./package/signal.py 中的打印语句没有被触发。这意味着加载的信号是来自标准库的信号。

5
重命名软件包以避免名称冲突有什么问题吗? - S.Lott
1
RHEL 没有问题。你的建议应该是推荐使用 make altinstall,因为升级 Python 会破坏 RHEL 5 系统,需要将 2.4 设置为 /usr/bin/python,否则 yum 将停止工作。更好的选择是从 EPEL 获取 Python 2.6。他不需要升级 Python,那么为什么要从稳定的操作系统转换呢? - jordanm
但是我得到了以下输出:$ python2 -m package.module => 加载 module.py 加载 signal.py - Felix Yan
@FelixYan:你在运行Python 2.4吗? - Bill Lynch
@Felix Yan:-m 仅在 Python2.6+ 上正常工作。http://docs.python.org/whatsnew/2.6.html#pep-366-explicit-relative-imports-from-a-main-module - jfs
显示剩余2条评论
3个回答

6
在这种情况下存在的问题是内置的 signal 模块作为解释器启动过程的一部分被导入。因此,当在 module.py 中运行您的代码时,sys.modules 下已经有了一个名为 signal 的条目,其值为内置模块。由于 sys.modules 是 import 语句查找的第一个地方,它将返回该内置模块并停止在那里。它甚至都不会去寻找您自己的自定义 signal.py
我可以想到三个解决方案:
  1. Rename your module. This is the easiest solution. You don't have to worry about your module names clashing with installed packages (eggs), because normally the import statement will search directories in the order specified by sys.path, and the current working directory when you run the interpreter is first on that list. signal is sort of a special case because it gets imported automatically before your code runs, but there are a limited number of modules for which that is true, and those are the only ones you have to avoid name clashes with.
  2. You can del sys.modules['signal'] and replace the entry with your own custom signal module. Don't actually do this unless your module is truly meant to be a drop-in replacement for the built-in signal. If you do, then any code that runs import signal from that point on will get your version, not the built-in one, and that could wreak havoc on any internal code that needs the signal module to function.
  3. You can use the imp module to get access to the internals of the import function, or at least equivalent code, which will allow you to bypass the check of sys.modules.

    If you really do need to do this, here's a code sample you could use:

    import imp, sys
    
    f, path, desc = imp.find_module('signal', sys.path)
    if f:
        f.close()
        signal = imp.new_module('signal')
        execfile(path, signal.__dict__)
    else:
        raise ImportError('signal.py not found')
    # signal is your module
    

    This code snippet is roughly equivalent to import signal except that it does not insert the module found into sys.modules, and it does not look for builtin modules before searching the path.


这真的很糟糕。谢谢你的解释。 - Bill Lynch

0

从项目目录(package 目录的父级目录)开始:

python -mpackage.state # Python 2.6+
python2.4 -c'from package.state import test; test()'
  • 在你的模块中始终使用绝对导入,例如 import package.signal 而不是 import signal,除非你知道自己在做什么
  • 确保 package 的父目录在 sys.path 中。 unittest2nosepytest 测试运行器可能会为您完成此操作
  • 避免与标准库模块冲突的模块名称。许多测试运行器和代码分析器都存在问题,并将正在处理的目录添加到 sys.path
  • python package/stat.py 会将 package 添加到 sys.path 中(请参见 http://bugs.python.org/issue13475)。在这种情况下,您不希望出现这种情况

-1

文件层次结构

.
|-- package
|   |-- __init__.py
|   |-- module.py
|   `-- signal.py
`-- script
`-- __init__.py

script.py

#!/usr/bin/env python
from package import module

package/module.py

#!/usr/bin/env python
print 'Loading module.py'
from package import signal

package/signal.py

#!/usr/bin/env python
print 'Loading signal.py'

输出:

$ ./script 
Loading module.py
Loading signal.py

编辑

刚刚在 Python 2.4.3 上通过修改 module.py 进行了测试。


@sharth - 抱歉,我刚刚做了一个更改并在Python 2.4.3上进行了测试。 - jdi
我已经更新了代码片段,加入了新的输出。我同意 ./script 可以运行,这一点一直都是如此。而且,我也想能够执行 ./package/signal.py。 - Bill Lynch

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