Как получить доступ к символам в __init__.py из __main__.py? - PullRequest
0 голосов
/ 30 мая 2019

У меня есть модуль - давайте назовем его foo - и я хочу, чтобы его можно было использовать с помощью вызова python -m foo.Моя программа выглядит следующим образом:

my_project
├──  foo
│   └── __init__.py
└── my_program.py

В __init__.py У меня есть код, который я запускаю при вызове python -m foo:

def bar(name):
    print(name)

# -- code used to 'run' the module
def main(name):
    bar("fritz")

if __name__ == "__main__":
    main()

Поскольку у меня достаточно кода выполненияв __init__.py сейчас (argparse материал и некоторая логика) я хочу разделить его на __main__.py:

my_project
├──  foo
│   ├── __init__.py
│   └── __main__.py
└── my_program.py

Несмотря на то, что это выглядит очень просто для меня, мне не удалось импортировать материал, расположенныйв __init__.py с __main__.py пока.

Я знаю - если foo находится в site-packages или доступен через PYTHONPATH, я могу просто import foo ..

Нов случае, если я хочу выполнить __main__.py напрямую (например, из некоторой IDE) с foo, расположенным в любом месте (т.е. не в папке, где Python ищет пакеты) - есть ли способ импортировать foo (__init__.py из того же каталога)?

Я пробовал import . и import foo - но оба подхода терпят неудачу (потому что они, конечно, означают что-то другое, конечно)

То, что я могу сделать - по крайней мере, чтобы объяснить мою цель - примерно так:

sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
import foo

Работает, но уродливо и немного опасно, так какЯ даже не знаю, действительно ли я импортирую foo из того же каталога ..

1 Ответ

0 голосов
/ 30 мая 2019

Вы можете вручную установить состояние импорта модуля , как если бы __main__.py было выполнено с помощью -m:

# foo/__main__.py
import os
import sys
if __package__ is None and __name__ == "__main__":  # executed without -m
   # set special attributes as if part of the package
   __file__ = os.path.abspath(__file__)
   __package__ = os.path.basename(os.path.dirname(__file__))
   # replace import path for __main__ with path for package
   main_path = os.path.dirname(__file__)
   try:
       index = sys.path.index(dir_path)
       if index != 0 or index != 1:
           raise ValueError('expected script directory after current directory or matching it')
   except ValueError:
       raise RuntimeError('sys.path does not include script directory as expected')
   else:
       sys.path[index] = main_path
# import regularly
from . import bar

Это использует, что python3 path/to/foo/__main__.py выполняет __main__ какавтономный скрипт: __package__ равен None, а __name__ также не включает пакет.Путь поиска в этом случае - <current directory>, <__main__ directory>, ..., хотя он свернут, если они совпадают: index равен либо 0, либо 1.

Как и во всех хитростях на внутренних устройствах, существует некоторое переходное состояниегде инварианты нарушены.Не выполняйте импорт до исправления модуля!

...