Невозможно загрузить относительный файл конфигурации с помощью ConfigParser из подкаталога

У меня есть следующая структура каталогов:

my_program/
       foo.py
       __init__.py # empty
       conf/
          config.cfg
          __init__.py 

В foo.py у меня есть это:

import sys 
#sys.path.append('conf/')
import ConfigParser

config = ConfigParser.ConfigParser()
config.read( 'conf/config.cfg' )

В conf/__init__.py у меня есть

__all__ = ["config.cfg"]

Я получаю эту ошибку в foo.py, которую я могу исправить, указав полный путь, но не тогда, когда я просто положил conf/config.cfg, но я хочу, чтобы относительный путь работал:

ConfigParser.NoSectionError

что фактически означает, что файл не может быть загружен (поэтому он не может прочитать раздел).

Я пробовал комментировать/не комментировать sys.path.append('conf/') в foo.py, но ничего не делает.

Любые идеи? Большое спасибо.

Ответы

Ответ 1

Пути относятся к текущему рабочему каталогу, который обычно является каталогом, из которого вы запускаете свою программу (но текущий каталог может быть изменен вашей программой [или модулем], и это вообще не каталог вашей программы файл).

Решение состоит в автоматическом вычислении пути к вашему файлу с помощью переменной __file__, которую интерпретатор Python создает для вас в foo.py:

import os
config.read(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'conf', 'config.cfg'))

Объяснение: переменная __file__ каждой программы (модуля) содержит свой путь (относительно текущего каталога, когда она была загружена, я думаю - я не мог найти ничего убедительного в документации Python). Преобразование его в абсолютный путь лучше справляется с общим случаем, когда модуль меняет текущий рабочий каталог до config.read() и где __file__ - относительный путь (который происходит, например, когда foo.py импортируется из его собственного каталога).

Таким образом, импорт корректно работает независимо от текущего рабочего каталога и где бы вы не разместили свой пакет.

PS: Боковое примечание: __all__ = ["config.cfg"] не то, что вы хотите: он сообщает Python, какие символы (переменные, функции) импортируются, когда вы делаете from conf import *. Его следует удалить.