Сериализация функции python с зависимостями

Я пробовал несколько подходов к расчетом функции python с зависимостями, следуя многим рекомендациям по StackOverflow (например, укроп, cloudpickle и т.д.), но все, похоже, сталкиваются с фундаментальной проблемой, которую я не могу понять.

У меня есть основной модуль, который пытается раскрыть функцию из импортированного модуля, отправляет его поверх ssh, который будет незакрашен и выполнен на удаленном компьютере.

Итак, main имеет:

    import dill (for example)
    import modulea

    serial=dill.dumps( modulea.func )
    send (serial)

На удаленном компьютере:

        import dill
        receive serial
        funcremote = dill.loads( serial )
        funcremote()

Если функции, замаринованные и отправленные, являются функциями верхнего уровня, определенными в самой себе, все работает. Когда они находятся в импортированном модуле, функция загрузки выходит из строя с сообщениями типа "модуль модуля не найден".

Похоже, что имя модуля маринуется вместе с именем функции. Я не вижу никакого способа "исправить" рассол, чтобы удалить зависимость или, альтернативно, создать в приемнике фиктивный модуль, чтобы стать получателем раскалывания.

Любые указатели будут высоко оценены.

- прасанна

Ответ 1

Я автор dill. Я делаю эту вещь более ssh, но с успехом. В настоящее время dill и любой другой сериализатор распиливают модули по ссылке... поэтому для успешного прохождения функции, определенной в файле, вы должны убедиться, что соответствующий модуль также установлен на другой машине. Я не верю, что есть какой-либо сериализатор объектов, который напрямую сериализует модули (т.е. Не по ссылке).

Сказав, что dill имеет некоторые опции для сериализации зависимостей объектов. Например, для экземпляров класса значением по умолчанию в dill является не сериализация экземпляров класса по ссылке... поэтому определение класса также может быть сериализовано и отправлено с экземпляром. В dill вы также можете (использовать очень новую функцию) сериализовать дескрипторы файлов, сериализируя файл, вместо того, чтобы делать это по ссылке. Но опять же, если у вас есть случай функции, определенной в модуле, вам не повезло, так как модули сериализованы по ссылке довольно чертовски универсально.

Возможно, вы сможете использовать dill для этого, просто не с травлением объекта, а с извлечением источника и отправкой исходного кода. В pathos.pp и pyina, dill нас использовали для извлечения исходных и зависимостей любого объекта (включая функции) и передачи их на другой компьютер/процесс/и т.д.. Однако, поскольку это нелегко, dill также может использовать переход на другой ресурс при попытке извлечь соответствующий импорт и отправить его вместо исходного кода.

Вы можете понять, надеюсь, это грязная грязная вещь (как указано в одной из зависимостей функции, которую я извлекаю ниже). Однако то, что вы запрашиваете, успешно выполняется в пакете pathos для передачи кода и зависимостей на разные машины через ssh-туннелированные порты.

>>> import dill
>>> 
>>> print dill.source.importable(dill.source.importable)
from dill.source import importable
>>> print dill.source.importable(dill.source.importable, source=True)
def _closuredsource(func, alias=''):
    """get source code for closured objects; return a dict of 'name'
    and 'code blocks'"""
    #FIXME: this entire function is a messy messy HACK
    #      - pollutes global namespace
    #      - fails if name of freevars are reused
    #      - can unnecessarily duplicate function code
    from dill.detect import freevars
    free_vars = freevars(func)
    func_vars = {}
    # split into 'funcs' and 'non-funcs'
    for name,obj in list(free_vars.items()):
        if not isfunction(obj):
            # get source for 'non-funcs'
            free_vars[name] = getsource(obj, force=True, alias=name)
            continue
        # get source for 'funcs'

#…snip… …snip… …snip… …snip… …snip… 

            # get source code of objects referred to by obj in global scope
            from dill.detect import globalvars
            obj = globalvars(obj) #XXX: don't worry about alias?
            obj = list(getsource(_obj,name,force=True) for (name,_obj) in obj.items())
            obj = '\n'.join(obj) if obj else ''
            # combine all referred-to source (global then enclosing)
            if not obj: return src
            if not src: return obj
            return obj + src
        except:
            if tried_import: raise
            tried_source = True
            source = not source
    # should never get here
    return

Я предполагаю, что что-то может быть также построено вокруг метода dill.detect.parents, который предоставляет список указателей на все родительские объекты для любого заданного объекта... и можно было бы восстановить все зависимости функций как объекты... но это не реализовано.

BTW: для создания туннеля ssh просто выполните следующее:

>>> t = pathos.Tunnel.Tunnel()
>>> t.connect('login.university.edu')
39322
>>> t  
Tunnel('-q -N -L39322:login.university.edu:45075 login.university.edu')

Затем вы можете работать через локальный порт с помощью ZMQ или ssh или что угодно. Если вы хотите сделать это с помощью ssh, pathos также имеет встроенную функцию.