Параллельный: импорт файла python из родственной папки.

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

working_dir\
    main.py
my_agent\
    my_worker.py
my_utility\
    my_utils.py

Код в каждом файле выглядит следующим образом

""" main.py """

import os, sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from my_agent.my_worker import MyWorker
import ray

ray.init()
workers = [MyWorker.remote(i) for i in range(10)]
ids = [worker.get_id.remote() for worker in workers]
# print(*ids, sep='\n')
print(*ray.get(ids), sep='\n')
""" worker.py """
from my_utility import my_utils
import ray

@ray.remote
class MyWorker():
    def __init__(self, id):
        self.id = id

    def get_id(self):
        return my_utils.f(self.id)
""" my_utils.py """
def f(id):
    return '{}: Everything is fine...'.format(id)

Вот часть полученного мной сообщения об ошибке

Отслеживание (последний вызов последний):

Файл "/Users/aptx4869/anaconda3/envs/p35/lib/python3.5/site-packages/ray/function_manager.py", строка 616, в fetch_and_register_actor unpickled_class = pickle.loads (pickled_class)

Файл "/Users/aptx4869/anaconda3/envs/p35/lib/python3.5/site-packages/ray/cloudpickle/cloudpickle.py", строка 894, в субимпорте import (имя)

ImportError: нет модуля с именем my_utility

Отслеживание (последний вызов последний):

Файл "main.py", строка 12, в печати (* ray.get (ids), sep = '\ n')

Файл "/Users/aptx4869/anaconda3/envs/p35/lib/python3.5/site-packages/ray/worker.py", строка 2377, получает значение повышения ray.worker.RayTaskError: ray_worker (pid = 30025, host = AiMacbook)

Исключение: актер с именем MyWorker не удалось импортировать, поэтому он не может выполнить этот метод.

Если я удалю все операторы, относящиеся к ray, приведенный выше код будет работать нормально. Поэтому я смело предполагаю, что причина в том, что ray запускает каждый субъект в новом процессе, а sys.path.append работает только в основном процессе. Поэтому я добавляю следующий код в worker.py

import os, sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

Но все равно не работает: появляется то же сообщение об ошибке. Теперь у меня закончились идеи, что мне делать?


person Maybe    schedule 24.01.2019    source источник
comment
Я считаю это os.path.dirname(os.path.dirname(__file__)) лучше, чем это os.path.join(os.path.dirname(__file__), '..')   -  person spaniard    schedule 24.01.2019
comment
@spaniard Спасибо :-) Думаю, вы предложили sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))). Но и это не работает ...   -  person Maybe    schedule 24.01.2019
comment
Вы пробовали убрать строчку if __name__ == '__main__' and __package__ is None:? а просто всегда добавлять к своему пути родительский каталог?   -  person spaniard    schedule 24.01.2019
comment
@spaniard Да, такая же история продолжается   -  person Maybe    schedule 24.01.2019
comment
@Darkonaut Я сделал то, что вы предложили, но все еще не работает. Я вспомнил, что python3 больше не требует __init__.py, не так ли?   -  person Maybe    schedule 24.01.2019
comment
Да, начиная с Python 3.3, он больше не нужен, но я предполагал, что лучу он может как-то понадобиться, поскольку это фреймворк.   -  person Darkonaut    schedule 24.01.2019
comment
@Darkonaut Спасибо. Я добавил __init__.py во все каталоги, даже в корневой каталог, содержащий эти три папки. Это все еще не работает.   -  person Maybe    schedule 24.01.2019
comment
Может быть конфликт имен. У вас есть utils.py и worker.py, и я вижу, что у Рэя тоже есть эти файлы. Переименуйте файлы с префиксом, чтобы избежать конфликта.   -  person Darkonaut    schedule 24.01.2019
comment
@Darkonaut Я сделал то, что вы предложили, и префикс всех относительных файлов / классов с помощью _1 _ ,,, все еще не работает ...   -  person Maybe    schedule 24.01.2019
comment
@Darkonaut Ага, я изменил все относительные имена ... Ошибка возникает в заявлении print. ИМХО, если ошибка вызвана конфликтом имен, не должно случиться так поздно.   -  person Maybe    schedule 24.01.2019
comment
Я не могу говорить от имени Ray, но, по крайней мере, в стандартных библиотеках multiprocessing.Pool необходимо, чтобы импорт используемых функций выполнялся для каждой распределенной задачи снова. Я не знаком с внутренним устройством Ray, но эта строка ImportError: No module named 'utils' заставляет меня задуматься. Похоже, он пытается импортировать утилиты из utils.py, а не из каталога.   -  person Darkonaut    schedule 24.01.2019
comment
@Darkonaut Я обновил код, имена файлов и сообщения об ошибках. Теперь можно увидеть, что ImportError относится к имени папки my_utility вместо my_utils.py файла. Насколько мне известно, луч вызывает from my_utility import my_utils в каждом новом процессе актора, и это вызывает эту проблему.   -  person Maybe    schedule 24.01.2019


Ответы (1)


Вы правы в том, в чем проблема.

В вашем примере вы изменяете sys.path в main.py, чтобы иметь возможность импортировать my_agent.my_worker и my_utility.my_utils.

Однако это изменение пути не распространяется на рабочие процессы, поэтому, если вы запустите удаленную функцию, например

@ray.remote
def f():
    # Print the PYTHONPATH on the worker process.
    import sys
    print(sys.path)

f.remote()

Вы увидите, что sys.path на рабочем сервере не включает добавленный вами родительский каталог.

Причина, по которой изменение sys.path на работнике (например, в конструкторе MyWorker) не работает, заключается в том, что определение класса MyWorker обрабатывается и отправляется рабочим. Затем рабочий распаковывает его, и процесс выделения определения класса требует, чтобы было импортировано my_utils, и это не удается, потому что конструктор актора еще не успел запуститься.

Здесь есть несколько возможных решений.

  1. Запустите скрипт с чем-то вроде

    PYTHONPATH=$(dirname $(pwd)):$PYTHONPATH python main.py
    

    (изнутри working_dir/). Это должно решить проблему, потому что в этом случае рабочие процессы разветвляются из процесса планировщика (который разветвляется из основного интерпретатора Python, когда вы вызываете ray.init(), и поэтому переменная среды будет унаследована рабочими (этого не происходит для sys.path предположительно потому, что это не переменная среды).

  2. Похоже на добавление строчки

    parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    os.environ["PYTHONPATH"] = parent_dir + ":" + os.environ.get("PYTHONPATH", "")
    

    in main.py (перед вызовом ray.init()) также работает по той же причине, что и выше.

  3. Рассмотрите возможность добавления setup.py и установки вашего проекта как пакета Python, чтобы он автоматически находился на соответствующем пути.

person Robert Nishihara    schedule 24.01.2019