Создание прерываемого процесса в питоне - PullRequest
3 голосов
/ 05 января 2011

Я создаю скрипт на python, который анализирует большой (но простой) CSV.

Это займет некоторое время для обработки.Я хотел бы иметь возможность прервать анализ CSV, чтобы я мог продолжить на более позднем этапе.

В настоящее время у меня есть это - который живет в большем классе: (незаконченный)

Редактировать:

У меня есть измененный код.Но система проанализирует более 3 миллионов строк.

def parseData(self)
    reader = csv.reader(open(self.file))
    for id, title, disc in reader:
        print "%-5s %-50s %s" % (id, title, disc)
        l = LegacyData()
        l.old_id = int(id)
        l.name = title
        l.disc_number = disc
        l.parsed = False
        l.save()

Это старый код.

def parseData(self):
        #first line start
        fields = self.data.next()
        for row in self.data:
            items = zip(fields, row)
            item = {}
            for (name, value) in items:
                item[name] = value.strip()
            self.save(item)

Спасибо, ребята.

Ответы [ 4 ]

2 голосов
/ 05 января 2011

Если под linux, нажмите Ctrl-Z и остановите запущенный процесс.Введите "fg", чтобы вернуть его и начать там, где вы его остановили.

1 голос
/ 05 января 2011

Как бы я это сделал:

Определяет фактический код обработки в классе, и в этом классе я реализую протокол Pickle (http://docs.python.org/library/pickle.html) (в основном, пишу правильные функции __getstate__ и __setstate__)

Этот класс будет принимать имя файла, сохранять открытый файл и экземпляр средства чтения CSV в качестве членов экземпляра. Метод __getstate__ сохранит текущую позицию файла, а setsets откроет файл, переместит его в нужную позицию и создаст новое средство чтения.

Я бы выполнил актуальную работу в методе __iter__, который передавал бы внешнюю функцию после обработки каждой строки.

Эта внешняя функция будет запускать вход мониторинга "основного цикла" для прерываний (сокетов, клавиатуры, состояния конкретного файла в файловой системе и т. Д.) - все будет тихо, просто вызовет следующую итерацию процессор. Если произойдет прерывание, оно будет отображать состояние процессора для конкретного файла на диске.

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

Здесь приведен некоторый (непроверенный) код - iea достаточно прост:

from cPickle import load, dump
import csv
import os, sys

SAVEFILE = "running.pkl"
STOPNOWFILE = "stop.now"

class Processor(object):
    def __init__(self, filename):
        self.file = open(filename, "rt")
        self.reader = csv.reader(self.file)
    def __iter__(self):
        for line in self.reader():
            # do stuff
            yield None
    def __getstate__(self):
        return (self.file.name, self.file.tell())
    def __setstate__(self, state):
        self.file = open(state[0],"rt")
        self.file.seek(state[1])
        self.reader = csv.reader(self.File)

def check_for_interrupts():
    # Use your imagination here!  
    # One simple thing would e to check for the existence of an specific file
    # on disk.
    # But you go all the way up to instantiate a tcp server and listen to 
    # interruptions on the network
    if os.path.exists(STOPNOWFILE): 
        return True
    return False

def main():
    if os.path.exists(SAVEFILE):
        with open(SAVEFILE) as savefile:
            processor = load(savefile)
        os.unlink(savefile)
    else:
        #Assumes the name of the .csv file to be passed on the command line
        processor = Processor(sys.argv[1])
    for line in processor:
        if check_for_interrupts():
            with open(SAVEFILE, "wb") as savefile:
                dump(processor)
            break

if __name__ == "__main__":
    main()
1 голос
/ 05 января 2011

Вы можете использовать signal, чтобы поймать событие.Это макет парсера, который может поймать CTRL-C на окнах и прекратить синтаксический анализ:

import signal, tme, sys

def onInterupt(signum, frame):
    raise Interupted()

try:
    #windows
    signal.signal(signal.CTRL_C_EVENT, onInterupt)
except:
    pass

class Interupted(Exception): pass
class InteruptableParser(object):

    def __init__(self, previous_parsed_lines=0):
        self.parsed_lines = previous_parsed_lines

    def _parse(self, line):
        # do stuff
        time.sleep(1) #mock up
        self.parsed_lines += 1
        print 'parsed %d' % self.parsed_lines

   def parse(self, filelike):
        for line in filelike:
            try:
                self._parse(line)
            except Interupted:
                print 'caught interupt'
                self.save()
                print 'exiting ...'
                sys.exit(0)

    def save(self):
        # do what you need to save state
        # like write the parse_lines to a file maybe
        pass

parser = InteruptableParser()
parser.parse([1,2,3])

Хотя я не могу его проверить, так как я сейчас нахожусь на Linux.

0 голосов
/ 16 января 2011

Мой полный код

Я последовал совету @jsbueno с флагом - но вместо другого файла я сохранил его в классе как переменную:

Я создаю класс - когда я вызываю его, запрашивает ЛЮБОЙ ввод, а затем начинает другой процесс, выполняющий мою работу. Поскольку это зациклено - если бы я должен был нажать клавишу, флаг устанавливается и проверяется только тогда, когда цикл вызывается для моего следующего анализа. Таким образом, я не убиваю текущее действие. Добавление флага process в базу данных для каждого объекта из данных, которые я вызываю, означает, что я могу запустить это в любое время и продолжить с того места, где остановился.

class MultithreadParsing(object):

    process = None
    process_flag = True

    def f(self):
        print "\nMultithreadParsing has started\n"
        while self.process_flag:
            ''' get my object from database '''
            legacy = LegacyData.objects.filter(parsed=False)[0:1]

            if legacy:
                print "Processing: %s %s" % (legacy[0].name, legacy[0].disc_number)
                for l in legacy:
                    ''' ... Do what I want it to do ...'''
                sleep(1)
            else:
                self.process_flag = False
                print "Nothing to parse"



    def __init__(self):
        self.process = Process(target=self.f)
        self.process.start()
        print self.process
        a = raw_input("Press any key to stop \n")
        print "\nKILL FLAG HAS BEEN SENT\n"

        if a:
            print "\nKILL\n"
            self.process_flag = False

Спасибо за все, что вы помогаете, ребята (особенно ваши @jsbueno) - если бы не вы, я бы не получил эту идею класса.

...