Ошибка при импорте существующего файла - "FileNotFoundError: [Errno 2] No such file or directory"
Я создаю простую текстовую игру.
Создал player.py в папке Players, и classes.yaml в папке yaml.
Мои папки:
Game
|__ yaml
|__ Players
В player.py я импортирую classes:
import yaml
with open("yaml/classes.yaml") as classes:
info = yaml.safe_load(classes.read())
И мне выдает ошибку:
File "<string>", line 3, in module
FileNotFoundError: [Errno 2] No such file or directory: 'yaml/classes.yaml'
Пробывал переименовывать, добавлять в другие папки - все тщетно. Хотелось бы оставить каждый файл на своем месте, если это возможно. Что пошло не так?
Ответы (2 шт):
Перед вашем кодом перейдите в каталог приложения
import sys, os
if __name__ == "__main__":
if getattr(sys, 'frozen', False):
# frozen
appdir = os.path.dirname(sys.executable)
else:
# plain
appdir = os.path.dirname(os.path.realpath(__file__))
os.chdir(os.path.abspath(appdir))
Причина проблемы -- в рабочей директории.
При запуске player.py рабочей будет папка с player.py, а при указании относительного пути, оно будет строиться относительно рабочей директории, т.е. файл будет искаться по пути: Players/yaml/classes.yaml.
Один из вариантов это сделать путь в player.py с указанием родительской папки:
import yaml
with open("../yaml/classes.yaml") as classes:
info = yaml.safe_load(classes.read())
Но у него есть минус. Допустим вы решите запустить main.py из папки Game/, а в этом скрипте сделать импорт players.player.py, в player.py будет загрузка с "../yaml/classes.yaml", но рабочая папка теперь другая, а именно -- Game/, поэтому /yaml/classes.yaml не будет найден, т.к. поиск будет в папке выше Game/.
Для решения этого нужно или указывать абсолютные пути (но это не переносимо между компьютерами) или использовать относительные пути, но в одном месте содержать путь к папке проекта и относительно этого пути строить другие.
Решаем проблему доступа к файлам проекта
Сделал новый проект и построил в нем такую структуру:
├── players/
│ ├── __init__.py
│ ├── player.py
│
├── yaml/
│ ├── classes.yaml
│
├── config.py
├── main.py
Где:
players/__init__.py: пустой, он нужно только для того, чтобы папкаplayer/считалась пакетомplayers/player.py:import config import yaml with open(config.DIR_YAML_CLASSES) as classes: info = yaml.safe_load(classes.read()) print(info)config.py, тут формируется общий путь относительно папки проекта:import inspect import os import sys from pathlib import Path # SOURCE: https://github.com/gil9red/SimplePyScripts/blob/927af3d3f05a0129338b21d074ca241c8a055118/get_current_script_dir.py def get_current_script_dir(follow_symlinks=True, normcase=False) -> str: # py2exe, PyInstaller, cx_Freeze if getattr(sys, 'frozen', False): path = os.path.abspath(sys.executable) else: # Analog inspect.getabsfile without os.path.normcase path = inspect.getframeinfo(inspect.currentframe()).filename path = os.path.abspath(path) if follow_symlinks: path = os.path.realpath(path) if normcase: path = os.path.normcase(path) return os.path.dirname(path) DIR = Path(get_current_script_dir()) DIR_YAML = DIR / 'yaml' DIR_YAML_CLASSES = DIR_YAML / "classes.yaml"yaml/classes.yaml:classes: [Mage, Knight]main.py, сделаем импортplayer.py:import players.player
ℹ️ При запуске main.py и player.py будет выведено {'classes': ['Mage', 'Knight']}, что говорит о том, что файл был успешно найден
ℹ️ Может случиться ситуация импорта скрипта, который находится, например, папкой выше. Для этого можно сделать добавление пути в его папку в sys.path (при импорте в указанным в нем папках и будет поиск), пример:
import sys
import os.path
# Добавление папки выше рабочей
sys.path.append('..')
# или указывая абсолютный, чтобы путь был более понятным при выводе всех путей
sys.path.append(os.path.abspath('..'))
# Вывести все пути, где поиск при импорте
print(*sys.path, sep='\n')