Скрипт Python 3, использующий относительный импорт при стандартном вводе, выдает ошибку: нет модуля с именем '__main__.XXX';«__main__» не является пакетом - PullRequest
1 голос
/ 29 апреля 2019

Скрипт Python 3, использующий относительный импорт при стандартном вводе, выдает ошибку: нет модуля с именем ' main .XXX';' main ' не является пакетом

Если у меня есть сценарий Python 3, который использует оператор относительного импорта в форме

from .subscript2 import subfunc2

А некоторые другие сценарии импортируютПриведенный выше скрипт как модуль работает без проблем.Однако, когда я выполняю скрипт, содержащий вышеуказанную строку, при стандартном вводе в интерпретатор Python, я получаю сообщение об ошибке вида:

ModuleNotFoundError: No module named '__main__.subscript2'; '__main__' is not a package

Причина, по которой мне нужно использовать stdin, заключается в том, что мне нужно использоватьElpy (https://elpy.readthedocs.io/en/latest/), который позволяет мне оценить этот скрипт (используя привязку Cc Cc) и затем добавить дополнительный код Python для вызова функций в этом скрипте, аналогично команде pdb 'взаимодействовать'.

Ниже приведен автономный скрипт для демонстрации происходящего:

#!/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 через stdin, он снова сломается, если я не пройду через эти скрипты и временно не удалю начальный "." (что создает трудности и подвержен ошибкам).

Итак, есть ли способ заставить эту работудля всех этих случаев?У меня есть возможность изменить инструмент, который я использую (тот, который передает скрипт в stdin Python), чтобы добавить дополнительный код в этот поток stdin, за до реального скрипта, но если это решениекакие утверждения заставили бы его работать?

Полезный справочный материал, помогающий в понимании, но который не выявил решения:

Ответы [ 2 ]

1 голос
/ 29 апреля 2019

Вам нужно использовать -m subdir.subscript1 из topdir.Папка subdir является пакетом, и когда вы используете -m, вам нужно дать полное имя модулю, который вы хотите запустить, или он не может понять, что он должен быть частью пакета, а немодуль верхнего уровня.

Попробуйте это:

echo
echo ////////////////////////////////////////////////////////////////////////////////
echo Using -m on subscript1.py:
echo ////////////////////////////////////////////////////////////////////////////////
cd /tmp/topdir                        # note, changed folder here
python -m subdir.subscript1           # add package name prefix here
0 голосов
/ 09 июня 2019

Я потратил много времени на поиск умных альтернатив, позволяющих мне делать то, что я хочу, и все решения, которые я мог придумать, были слишком сложны для применения на практике, поэтому:

В итоге я избежал этого, используя символические ссылки в том же каталоге, в котором находится скрипт верхнего уровня. Это не здорово, но лучше, чем менять PYTHONPATH в рабочей среде. Поэтому, если у меня есть служебные модули, хранящиеся в моей директории ~ / bin / python, но я на самом деле работаю над сценарием, скажем, ~ / some_project_dir / my_top_level.py, я создаю символическую ссылку, например:

ln -s ~/bin/python ~/some_project_dir/python

затем внутри файла my_top_level.py я использую операторы импорта, такие как:

from python.file_utils import read_lines_from_file
lines = read_lines_from_file("/tmp/some_file")

Так что, если я когда-нибудь переместлю ~ / some_project_dir в какой-либо другой каталог, символическая ссылка будет идти вместе с ним. Если бы я когда-либо переместил каталог ~ / bin / python, то у меня была бы рутинная работа по обновлению символических ссылок, но это небольшая рутинная работа, и не так уж и много.

...