在标准输入上使用相对导入的 Python 3 脚本给出错误:没有名为“ main .XXX”的模块;' main ' 不是一个包
如果我有一个使用以下形式的相对导入语句的 Python 3 脚本
from .subscript2 import subfunc2
而其他一些脚本将上述脚本作为模块导入,它可以正常工作。但是,当我在 Python 解释器的标准输入上执行包含上述行的脚本时,我收到以下形式的错误:
ModuleNotFoundError: No module named '__main__.subscript2'; '__main__' is not a package
我需要使用标准输入的原因是因为我需要使用 Elpy ( https://elpy.readthedocs.io/en/latest/ ),它允许我评估该脚本(使用 Cc Cc 键绑定),然后添加其他在该脚本中调用函数的 Python 代码,类似于 pdb 'interact' 命令。
下面是一个独立的脚本来演示正在发生的事情:
#!/bin/bash
rm -rf /tmp/topdir
mkdir /tmp/topdir
mkdir /tmp/topdir/subdir
unset PYTHONPATH
cat > /tmp/topdir/topscript.py <<'EOF'
from subdir.subscript1 import subfunc1
EOF
touch /tmp/topdir/subdir/__init__.py
cat > /tmp/topdir/subdir/subscript1.py <<'EOF'
from .subscript2 import subfunc2
def subfunc1():
subfunc2()
print('subscript1 loaded successfully')
EOF
cat > /tmp/topdir/subdir/subscript2.py <<'EOF'
import os
def subfunc2():
print('subfunc2 called. Current working directory: {}'.format(os.getcwd()))
print('subscript2 loaded successfully')
subfunc2()
EOF
echo
echo ////////////////////////////////////////////////////////////////////////////////
echo Python version:
echo ////////////////////////////////////////////////////////////////////////////////
python --version
cd /tmp
echo
echo ////////////////////////////////////////////////////////////////////////////////
echo Direct execution of topscript.py from inside $(pwd)
echo ////////////////////////////////////////////////////////////////////////////////
python /tmp/topdir/topscript.py
cd /tmp/topdir
echo
echo ////////////////////////////////////////////////////////////////////////////////
echo Using stdin on topscript.py from inside $(pwd):
echo ////////////////////////////////////////////////////////////////////////////////
python < topscript.py
cd /tmp/topdir/subdir
echo
echo ////////////////////////////////////////////////////////////////////////////////
echo Using stdin on subscript1.py from inside $(pwd):
echo ////////////////////////////////////////////////////////////////////////////////
python < subscript1.py
cd /tmp/topdir/subdir
echo
echo ////////////////////////////////////////////////////////////////////////////////
echo Using -m subscript1 from inside $(pwd):
echo ////////////////////////////////////////////////////////////////////////////////
python -m subscript1
cd /tmp/topdir/subdir
echo
echo ////////////////////////////////////////////////////////////////////////////////
echo Using stdin on subscript2.py from inside $(pwd):
echo ////////////////////////////////////////////////////////////////////////////////
python < subscript2.py
# This one was added in response to https://stackoverflow.com/a/55895684/257924
cd /tmp/topdir
echo
echo ////////////////////////////////////////////////////////////////////////////////
echo Using -m on subdir.subscript1 from inside $(pwd):
echo ////////////////////////////////////////////////////////////////////////////////
python -m subdir.subscript1
# This one is a variation of https://stackoverflow.com/a/55895684/257924 using symlinks:
cd /tmp/topdir/subdir
echo
echo ////////////////////////////////////////////////////////////////////////////////
echo "Using -m on selfdir.subscript1 from inside $(pwd) (where selfdir is a symlink):"
echo ////////////////////////////////////////////////////////////////////////////////
ln -s ../subdir selfdir
python -m selfdir.subscript1
这是上面的输出:
////////////////////////////////////////////////////////////////////////////////
Python version:
////////////////////////////////////////////////////////////////////////////////
Python 3.7.3
////////////////////////////////////////////////////////////////////////////////
Direct execution of topscript.py from inside /tmp
////////////////////////////////////////////////////////////////////////////////
subscript2 loaded successfully
subfunc2 called. Current working directory: /tmp
subscript1 loaded successfully
////////////////////////////////////////////////////////////////////////////////
Using stdin on topscript.py from inside /tmp/topdir:
////////////////////////////////////////////////////////////////////////////////
subscript2 loaded successfully
subfunc2 called. Current working directory: /tmp/topdir
subscript1 loaded successfully
////////////////////////////////////////////////////////////////////////////////
Using stdin on subscript1.py from inside /tmp/topdir/subdir:
////////////////////////////////////////////////////////////////////////////////
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named '__main__.subscript2'; '__main__' is not a package
////////////////////////////////////////////////////////////////////////////////
Using -m subscript1 from inside /tmp/topdir/subdir:
////////////////////////////////////////////////////////////////////////////////
Traceback (most recent call last):
File "/home/drunkard/conda/Ubuntu.18.04.miniconda3/envs/envpython3/lib/python3.7/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/home/drunkard/conda/Ubuntu.18.04.miniconda3/envs/envpython3/lib/python3.7/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/tmp/topdir/subdir/subscript1.py", line 1, in <module>
from .subscript2 import subfunc2
ImportError: attempted relative import with no known parent package
////////////////////////////////////////////////////////////////////////////////
Using stdin on subscript2.py from inside /tmp/topdir/subdir:
////////////////////////////////////////////////////////////////////////////////
subscript2 loaded successfully
subfunc2 called. Current working directory: /tmp/topdir/subdir
////////////////////////////////////////////////////////////////////////////////
Using -m on subdir.subscript1 from inside /tmp/topdir:
////////////////////////////////////////////////////////////////////////////////
subscript2 loaded successfully
subfunc2 called. Current working directory: /tmp/topdir
subscript1 loaded successfully
////////////////////////////////////////////////////////////////////////////////
Using -m on selfdir.subscript1 from inside /tmp/topdir/subdir (where selfdir is a symlink):
////////////////////////////////////////////////////////////////////////////////
subscript2 loaded successfully
subfunc2 called. Current working directory: /tmp/topdir/subdir
subscript1 loaded successfully
我尝试了各种方法,但无法提出解决方案。我能做的最好的就是删除最初的“。” 像这样:
from subscript2 import subfunc2
但它破坏了正常执行:
////////////////////////////////////////////////////////////////////////////////
Python version:
////////////////////////////////////////////////////////////////////////////////
Python 3.7.3
////////////////////////////////////////////////////////////////////////////////
Direct execution of topscript.py from inside /tmp
////////////////////////////////////////////////////////////////////////////////
Traceback (most recent call last):
File "/tmp/topdir/topscript.py", line 1, in <module>
from subdir.subscript1 import subfunc1
File "/tmp/topdir/subdir/subscript1.py", line 1, in <module>
from subscript2 import subfunc2
ModuleNotFoundError: No module named 'subscript2'
////////////////////////////////////////////////////////////////////////////////
Using stdin on topscript.py from inside /tmp/topdir:
////////////////////////////////////////////////////////////////////////////////
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/tmp/topdir/subdir/subscript1.py", line 1, in <module>
from subscript2 import subfunc2
ModuleNotFoundError: No module named 'subscript2'
////////////////////////////////////////////////////////////////////////////////
Using stdin on subscript1.py from inside /tmp/topdir/subdir:
////////////////////////////////////////////////////////////////////////////////
subscript2 loaded successfully
subfunc2 called. Current working directory: /tmp/topdir/subdir
subscript1 loaded successfully
////////////////////////////////////////////////////////////////////////////////
Using -m subscript1 from inside /tmp/topdir/subdir:
////////////////////////////////////////////////////////////////////////////////
subscript2 loaded successfully
subfunc2 called. Current working directory: /tmp/topdir/subdir
subscript1 loaded successfully
////////////////////////////////////////////////////////////////////////////////
Using stdin on subscript2.py from inside /tmp/topdir/subdir:
////////////////////////////////////////////////////////////////////////////////
subscript2 loaded successfully
subfunc2 called. Current working directory: /tmp/topdir/subdir
////////////////////////////////////////////////////////////////////////////////
Using -m on subdir.subscript1 from inside /tmp/topdir:
////////////////////////////////////////////////////////////////////////////////
Traceback (most recent call last):
File "/home/drunkard/conda/Ubuntu.18.04.miniconda3/envs/envpython3/lib/python3.7/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/home/drunkard/conda/Ubuntu.18.04.miniconda3/envs/envpython3/lib/python3.7/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/tmp/topdir/subdir/subscript1.py", line 1, in <module>
from subscript2 import subfunc2
ModuleNotFoundError: No module named 'subscript2'
////////////////////////////////////////////////////////////////////////////////
Using -m on selfdir.subscript1 from inside /tmp/topdir/subdir (where selfdir is a symlink):
////////////////////////////////////////////////////////////////////////////////
subscript2 loaded successfully
subfunc2 called. Current working directory: /tmp/topdir/subdir
subscript1 loaded successfully
为了完成上述工作,我必须PYTHONPATH
在环境中设置以冒号分隔的目录列表以及我使用的所有脚本。这对于我自己的脚本来说是可行的,但是只要我想通过标准输入将其他已发布的脚本读入 python,它就会再次中断,除非我通过这些脚本并删除初始的“。” 暂时(这是一个麻烦和容易出错)。
那么,有没有办法让所有这些情况都有效?我确实可以选择更改我正在使用的工具(将脚本提供给 python 的标准输入的工具)以 在实际脚本之前向该标准输入流添加额外的代码,但如果这是解决方案,什么语句会强制它去工作?
有助于理解的有用参考资料,但这并没有揭示解决方案: