Ответ 1
В этом конкретном случае предупреждение двойного импорта происходит из-за этой строки в proj/__init__.py
:
from .proj import main
Что означает эта строка, так это то, что к тому моменту, -m
реализация переключателя -m
завершает шаг import proj
, proj.proj
уже импортирован как побочный эффект импорта родительского пакета.
Избегание предупреждения
Чтобы избежать предупреждения, вам нужно найти способ гарантировать, что импорт родительского пакета не подразумевает импорт пакета, выполняемого с -m
переключателя -m
.
Два основных варианта решения этой проблемы:
- Отбросьте
from.proj import main
линииfrom.proj import main
(как предложил @John Moutafis), предполагая, что это можно сделать без нарушения совместимости API; или -
Удалите,
if __name__ == "__main__":
блок изproj
субмодуля и заменить его на отдельныйproj/__main__.py
файл, который просто делает:from .proj import main main()
Если вы перейдете с опцией 2, то вызов командной строки также изменится, чтобы быть просто python -m proj
, а не ссылаться на подмодуль.
Более обратный вариант варианта 2 заключается в том, чтобы добавить __main__.py
не удаляя блок CLI из текущего подмодуля, и это может быть особенно хорошим подходом в сочетании с DeprecationWarning
:
if __name__ == "__main__":
import warnings
warnings.warn("use 'python -m proj', not 'python -m proj.proj'", DeprecationWarning)
main()
Если proj/__main__.py
уже используется для каких-то других целей, вы также можете делать такие вещи, как замена python -m proj.proj
на python -m proj.proj_cli
, где proj/proj_cli.py
выглядит так:
if __name__ != "__main__":
raise RuntimeError("Only for use with the -m switch, not as a Python API")
from .proj import main
main()
Почему существует предупреждение?
Это предупреждение -m
когда реализация коммутатора -m
собирается снова запустить и уже импортированный код модуля в модуле __main__
, что означает, что у вас будет две разные копии всего, что он определяет, - классы, функции, контейнеры и т.д.
В зависимости от специфики приложения это может работать нормально (именно поэтому это предупреждение, а не ошибка), или это может привести к причудливому поведению, например, изменения состояния уровня модуля, которые не будут разделены, как ожидалось, или даже исключения, которые не были пойманы, обработчик исключений пытался поймать тип исключения из одного экземпляра модуля, в то время как возникшее исключение использовало тип из другого экземпляра.
Следовательно, неопределенное this may cause unpredictable behaviour
предупреждение о this may cause unpredictable behaviour
- если что-то пойдет не так, как результат выполнения кода верхнего уровня модуля дважды, симптомы могут быть почти что угодно.
Как вы можете отлаживать более сложные случаи?
Хотя в этом конкретном примере импорт побочных эффектов находится непосредственно в proj/__init__.py
, там гораздо более тонкий и трудно отлаживаемый вариант, где вместо этого выполняется родительский пакет:
import some_other_module
а затем это some_other_module
(или модуль, который он импортирует), который делает:
import proj.proj # or "from proj import proj"
Предполагая, что неправильное поведение воспроизводимо, основным способом отладки этих проблем является запуск python в подробном режиме и проверка последовательности импорта:
$ python -v -c "print('Hello')" 2>&1 | grep '^import'
import zipimport # builtin
import site # precompiled from /usr/lib64/python2.7/site.pyc
import os # precompiled from /usr/lib64/python2.7/os.pyc
import errno # builtin
import posix # builtin
import posixpath # precompiled from /usr/lib64/python2.7/posixpath.pyc
import stat # precompiled from /usr/lib64/python2.7/stat.pyc
import genericpath # precompiled from /usr/lib64/python2.7/genericpath.pyc
import warnings # precompiled from /usr/lib64/python2.7/warnings.pyc
import linecache # precompiled from /usr/lib64/python2.7/linecache.pyc
import types # precompiled from /usr/lib64/python2.7/types.pyc
import UserDict # precompiled from /usr/lib64/python2.7/UserDict.pyc
import _abcoll # precompiled from /usr/lib64/python2.7/_abcoll.pyc
import abc # precompiled from /usr/lib64/python2.7/abc.pyc
import _weakrefset # precompiled from /usr/lib64/python2.7/_weakrefset.pyc
import _weakref # builtin
import copy_reg # precompiled from /usr/lib64/python2.7/copy_reg.pyc
import traceback # precompiled from /usr/lib64/python2.7/traceback.pyc
import sysconfig # precompiled from /usr/lib64/python2.7/sysconfig.pyc
import re # precompiled from /usr/lib64/python2.7/re.pyc
import sre_compile # precompiled from /usr/lib64/python2.7/sre_compile.pyc
import _sre # builtin
import sre_parse # precompiled from /usr/lib64/python2.7/sre_parse.pyc
import sre_constants # precompiled from /usr/lib64/python2.7/sre_constants.pyc
import _locale # dynamically loaded from /usr/lib64/python2.7/lib-dynload/_localemodule.so
import _sysconfigdata # precompiled from /usr/lib64/python2.7/_sysconfigdata.pyc
import abrt_exception_handler # precompiled from /usr/lib64/python2.7/site-packages/abrt_exception_handler.pyc
import encodings # directory /usr/lib64/python2.7/encodings
import encodings # precompiled from /usr/lib64/python2.7/encodings/__init__.pyc
import codecs # precompiled from /usr/lib64/python2.7/codecs.pyc
import _codecs # builtin
import encodings.aliases # precompiled from /usr/lib64/python2.7/encodings/aliases.pyc
import encodings.utf_8 # precompiled from /usr/lib64/python2.7/encodings/utf_8.pyc
В этом конкретном примере показан только базовый набор импорта, который Python 2.7 на Fedora делает при запуске. При отладке двойного импорта RuntimeWarning
как в этом вопросе, вы будете искать строки "import proj", а затем "import proj.proj" в подробном выводе, а затем внимательно посмотрите на импорт, непосредственно предшествующий " import proj.proj ".