[Python] Декораторы run_async и run_subprocess.
От: Critical Error ICQ: 123736611
Дата: 18.03.09 06:07
Оценка: 10 (2)
Наткнулся тут недавно на декоратор run_async. Идея мне понравилась и я решил реализовать также декоратор run_subprocess. Пользоваться им исключительно просто: достаточно написать перед объявлением функции @run_subprocess и функция будет запускаться в отдельном процессе.

Например:
if __name__ == '__main__':
    @run_subprocess
    def print_somedata():
        from time import sleep
        print 'starting print_somedata'
        sleep(2)
        print 'print_somedata: 2 sec passed'
        sleep(2)
        print 'print_somedata: 2+2 sec passed'
        sleep(2)
        print 'finished print_somedata'

    def main():
        print_somedata()
        print 'back in main'
        print_somedata()
        print 'back in main'
        print_somedata()
        print 'back in main'

    main()


Вывод:
c:\Projects\Py>decorator.py
back in main
back in main
back in main

c:\Projects\Py>starting print_somedata
starting print_somedata
starting print_somedata
print_somedata: 2 sec passed
print_somedata: 2 sec passed
print_somedata: 2 sec passed
print_somedata: 2+2 sec passed
print_somedata: 2+2 sec passed
print_somedata: 2+2 sec passed
finished print_somedata
finished print_somedata
finished print_somedata


Исходник декоратора:
def run_subprocess(func):
    from subprocess import Popen,PIPE
    from functools import wraps
    import inspect
    import sys

    fname = inspect.getfile(func)+'.'+func.__name__+'.py'
    lines, startline = inspect.getsourcelines(func)
    nn = lines[0].find('@')

    @wraps(func)
    def async_func( *args, **kwargs):
        proc = Popen([fname,], executable=sys.executable, stdin=PIPE)
        fd = proc.stdin
        for line in lines[1:]:
            fd.write(line[nn:])
        fd.write(func.__name__+'(')
        for i in args:
            fd.write(repr(i)+', ')
        for a,b in kwargs.iteritems():
            fd.write(a+'='+repr(b)+', ')
        fd.write(')')
        return proc

    return async_func


Работает оно так: при старте модуля в декораторе сохраняется исходник декорируемой функции. Это можно сделать благодаря модулю inspect. Далее при старте функции запускается интерпретатор питона, на stdin которого подается исходник функции и параметры запуска. Декоратор возвращает экземпляр класса Popen, который можно сохранить например с целью подождать завершения процессов.

Решение не особо хорошее, но рабочее. Может ктонибудь предложит более элегантный подход?
python subprocess
Re: [Python] Декораторы run_async и run_subprocess.
От: Сергей  
Дата: 18.03.09 07:05
Оценка:
Здравствуйте, Critical Error, Вы писали:

CE>Решение не особо хорошее, но рабочее. Может ктонибудь предложит более элегантный подход?


В линуксе/бсд — просто fork().
Никаких плясок с сохранением исходника функции.
Re[2]: [Python] Декораторы run_async и run_subprocess.
От: Critical Error ICQ: 123736611
Дата: 18.03.09 09:10
Оценка:
Здравствуйте, Сергей, Вы писали:

С>В линуксе/бсд — просто fork().

С>Никаких плясок с сохранением исходника функции.

Так ведь fork создает копию процесса, но не позволяет вызвать конкретную функцию, и чтобы это сделать надо писать обработчик командной строки. У декоратора в этом плане огромный плюс — 1 строчка кода и все дела. Замечу еще, что этот декоратор позволяет также передавать аргументы функции. Чтобы это повторить с помощью форка понадобится написать примерно столько же кода, сколько в этом декораторе. Поэтому мне так и понравился этот подход.
Re[3]: [Python] Декораторы run_async и run_subprocess.
От: Сергей  
Дата: 18.03.09 09:24
Оценка:
Здравствуйте, Critical Error, Вы писали:

CE>Так ведь fork создает копию процесса, но не позволяет вызвать конкретную функцию, и чтобы это сделать надо писать обработчик командной строки. У декоратора в этом плане огромный плюс — 1 строчка кода и все дела. Замечу еще, что этот декоратор позволяет также передавать аргументы функции. Чтобы это повторить с помощью форка понадобится написать примерно столько же кода, сколько в этом декораторе. Поэтому мне так и понравился этот подход.


Я имею ввиду — для реализации твоего декоратора можно использовать fork(). Вместо Popen() со скармливанием исходника можно сделать fork() и вызов декорируемой функции в потомке.
Re[4]: [Python] Декораторы run_async и run_subprocess.
От: Critical Error ICQ: 123736611
Дата: 18.03.09 09:58
Оценка:
Здравствуйте, Сергей, Вы писали:

С>Я имею ввиду — для реализации твоего декоратора можно использовать fork(). Вместо Popen() со скармливанием исходника можно сделать fork() и вызов декорируемой функции в потомке.


То есть вот так будет работать?

if __name__ == '__main__':
    def print_somedata():
        from time import sleep
        print 'starting print_somedata'
        sleep(2)
        print 'print_somedata: 2 sec passed'

    def main():
        os.fork()          # после этого уже имеем 2 корректно работающих процесса
        print_somedata()
        print 'back in main'

    main()
Re[5]: [Python] Декораторы run_async и run_subprocess.
От: Сергей  
Дата: 18.03.09 10:11
Оценка:
Здравствуйте, Critical Error, Вы писали:

CE>То есть вот так будет работать?[...]


Ну там import os надо добавить, а так — работать будет.
Вот такой вывод:
starting print_somedata
starting print_somedata
print_somedata: 2 sec passed
back in main
print_somedata: 2 sec passed
back in main
Re[3]: [Python] Декораторы run_async и run_subprocess.
От: meandr  
Дата: 18.03.09 11:05
Оценка:
Re[2]: [Python] Декораторы run_async и run_subprocess.Ошибаетесь полноценный fork работает не так. Дочерний процесс запускаеться как раз сразу после исполнения fork. Поясню:

pid = fork()

if(pid == 0)
{
    //вот здесь уже child рабоает
}
else
{
    //а здесь все ещё paretn
};
Posted via RSDN NNTP Server 2.1 beta
Re[6]: [Python] Декораторы run_async и run_subprocess.
От: Critical Error ICQ: 123736611
Дата: 18.03.09 12:11
Оценка:
Здравствуйте, Сергей, Вы писали:

С>Здравствуйте, Critical Error, Вы писали:


CE>>То есть вот так будет работать?[...]


С>Ну там import os надо добавить, а так — работать будет.


Да, штука хорошая, жаль не мультиплатформенная. Или можно чтото подобное на винде сделать?

Кстати еще вот такой вопрос: как я понимаю форк делает клон процесса с расшаренными дискрипторами и памятью. Как в таком случае будет работать питон? Его объекты будут склонированы 1 к 1? Что если программа которая делает форк является GUI приложением? Появится ли клон окна? В общем хорошо ли это — клонировать все объекты питона в дочернем процессе?

В общем как мне кажется моя версия декоратора имеет право на жизнь и на линуксе тоже, так как позволяет создать "чистый" процесс без лишних объектов, который выполнял бы конкретную порученную ему задачу.
Re[4]: [Python] Декораторы run_async и run_subprocess.
От: Critical Error ICQ: 123736611
Дата: 18.03.09 12:18
Оценка:
Здравствуйте, meandr, Вы писали:

M>Ошибаетесь полноценный fork работает не так. Дочерний процесс запускаеться как раз сразу после исполнения fork. Поясню:


M>
M>pid = fork()

M>if(pid == 0)
M>{
M>    //вот здесь уже child рабоает
M>}
M>else
M>{
M>    //а здесь все ещё paretn
M>};
M>


Ага, ну в принципе можно сделать декоратор и на основе форка, жалко у меня нет линукса и протестить невозможно.
Re[7]: [Python] Декораторы run_async и run_subprocess.
От: Сергей  
Дата: 18.03.09 12:35
Оценка:
Здравствуйте, Critical Error, Вы писали:

CE>Да, штука хорошая, жаль не мультиплатформенная. Или можно чтото подобное на винде сделать?


В cygwin'е что-то там такое есть, как оно работает и насколько хорошо — мне неизвестно.

CE>Кстати еще вот такой вопрос: как я понимаю форк делает клон процесса с расшаренными дискрипторами и памятью. Как в таком случае будет работать питон? Его объекты будут склонированы 1 к 1?


Не совсем склонированы; насколько я понимаю — у процессов будут общие страницы памяти, которые будут копироваться при необходимости.
По поведению полученных процессов — примерно то же самое, как будто была скопирована вся память.
Я в тонкостях досконально не разбирался, могу и ошибаться.

CE>Что если программа которая делает форк является GUI приложением? Появится ли клон окна?


Вряд ли, скорее всего будут странные спецэффекты при одновременной работе с одним подключением к X-серверу.

CE>В общем хорошо ли это — клонировать все объекты питона в дочернем процессе?


Это не хорошо и не плохо, как обычно всё зависит от целей применения.
Например, в случае декоратора, реализованного fork'ом — функция будет иметь доступ к глобальным объектам. В твоей реализации — нет. Для чего-то хорошо одно, для чего-то другое.

CE>В общем как мне кажется моя версия декоратора имеет право на жизнь и на линуксе тоже, так как позволяет создать "чистый" процесс без лишних объектов, который выполнял бы конкретную порученную ему задачу.


Popen() все равно в итоге вызовет fork() для запуска нового процесса (на линуксе).
Твоя версия несомненно имеет плюс в виде портируемости.
Re[8]: [Python] Декораторы run_async и run_subprocess.
От: Critical Error ICQ: 123736611
Дата: 18.03.09 14:04
Оценка:
Здравствуйте, Сергей, Вы писали:

С>Это не хорошо и не плохо, как обычно всё зависит от целей применения.

С>Например, в случае декоратора, реализованного fork'ом — функция будет иметь доступ к глобальным объектам. В твоей реализации — нет. Для чего-то хорошо одно, для чего-то другое.

Это да... недостаток иногда. В общем вот код декоратора на основе форка. К сожалению затестить не могу так как использую винду.

def run_fork(func):
    from functools import wraps
    from os import fork

    @wraps(func)
    def async_func(*args, **kwargs):
        pid = fork()
        if pid == 0:
            func(*args, **kwargs)
        else:
            return pid

    return async_func
Re: [Python] Декораторы run_async и run_subprocess.
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.03.09 23:02
Оценка: 2 (1)
Здравствуйте, Critical Error, Вы писали:

CE>Работает оно так: при старте модуля в декораторе сохраняется исходник декорируемой функции. Это можно сделать благодаря модулю inspect. Далее при старте функции запускается интерпретатор питона, на stdin которого подается исходник функции и параметры запуска. Декоратор возвращает экземпляр класса Popen, который можно сохранить например с целью подождать завершения процессов.


CE>Решение не особо хорошее, но рабочее. Может ктонибудь предложит более элегантный подход?


На модуль multiprocessing (из поставки 2.6) не смотрели?
The God is real, unless declared integer.
Re[2]: [Python] Декораторы run_async и run_subprocess.
От: Critical Error ICQ: 123736611
Дата: 18.03.09 23:35
Оценка:
Здравствуйте, netch80, Вы писали:

N>На модуль multiprocessing (из поставки 2.6) не смотрели?


Да, посмотрел. Очень мощный модуль. Как я понял для старта он использует os.fork на линуксе и subprocess.Popen на винде. Отличие от моего декоратора заключается в том, что multiprocessing передает интерпретатору весь модуль вместо исходника одной конкретной функции. Непонятно лишь как они смогли заставить интепретатор выполнять нужную функцию.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.