У меня есть функция в Jython, эта функция использует Popen для запуска другой программы, которая записывает xml файл в stdout, который направлен на файл. Когда процесс завершен, я закрою файл и вызову другую функцию для его анализа. Я получаю кучу сообщений об ошибках, ссылающихся на доступ к закрытым файлам и/или неправильно отформатированные XML файлы (которые выглядят прекрасно, когда я смотрю на них) во время разбора. Я думал, что output.close() может вернуться до закрытия файла, и поэтому я добавил цикл, который ждал, что output.closed будет true. Сначала это работало, но затем моя программа напечатала следующие
blasting
blasted
parsing
parsed
Extending genes found via genemark, 10.00% done
blasting
blasted
parsing
Exception in thread "_CouplerThread-7 (stdout)" Traceback (most recent call last):
File "/Users/mbsulli/jython/Lib/subprocess.py", line 675, in run
self.write_func(buf)
IOError: java.nio.channels.AsynchronousCloseException
[Fatal Error] 17_2_corr.blastp.xml:15902:63: XML document structures must start and end within the same entity.
Retry
blasting
blasted
parsing
Exception in thread "_CouplerThread-9 (stdout)" Traceback (most recent call last):
File "/Users/mbsulli/jython/Lib/subprocess.py", line 675, in run
self.write_func(buf)
IOError: java.nio.channels.ClosedChannelException
[Fatal Error] 17_2_corr.blastp.xml:15890:30: XML document structures must start and end within the same entity.
Retry
blasting
Я не уверен, что мои варианты отсюда. Правильно ли я думал, что xml не написан до того, как я его разобрал? Если да, то кого я могу сделать, убедитесь, что это так.
def parseBlast(fileName):
"""
A function for parsing XML blast output.
"""
print "parsing"
reader = XMLReaderFactory.createXMLReader()
reader.entityResolver = reader.contentHandler = BlastHandler()
reader.parse(fileName)
print "parsed"
return dict(map(lambda iteration: (iteration.query, iteration), reader.getContentHandler().iterations))
def cachedBlast(fileName, blastLocation, database, eValue, query, pipeline, remote = False, force = False):
"""
Performs a blast search using the blastp executable and database in blastLocation on
the query with the eValue. The result is an XML file saved to fileName. If fileName
already exists the search is skipped. If remote is true then the search is done remotely.
"""
if not os.path.isfile(fileName) or force:
output = open(fileName, "w")
command = [blastLocation + "/bin/blastp",
"-evalue", str(eValue),
"-outfmt", "5",
"-query", query]
if remote:
command += ["-remote",
"-db", database]
else:
command += ["-num_threads", str(Runtime.getRuntime().availableProcessors()),
"-db", database]
print "blasting"
blastProcess = subprocess.Popen(command,
stdout = output)
while blastProcess.poll() == None:
if pipeline.exception:
print "Stopping in blast"
blastProcess.kill()
output.close()
raise pipeline.exception
output.close()
while not output.closed:
pass
print "blasted"
try:
return parseBlast(fileName)
except SAXParseException:
print 'Retry'
return cachedBlast(fileName, blastLocation, database, eValue, query, pipeline, remote, True)
Я думаю, что эта проблема началась, когда я переключился с вызова на подпроцесс на использование метода опроса, чтобы я мог остановить процесс во время его работы. Поскольку у меня уже были результаты для многих наборов данных, с которыми я работал, было некоторое время, прежде чем мне пришлось снова запустить подпроцесс, поэтому было трудно сказать. Во всяком случае, я предполагаю, что вывод все еще записывался, когда я его закрыл, и мое решение заключалось в том, чтобы переключиться на каналы и написать файл сам.
def cachedBlast(fileName, blastLocation, database, eValue, query, pipeline, remote = False, force = False):
"""
Performs a blast search using the blastp executable and database in blastLocation on
the query with the eValue. The result is an XML file saved to fileName. If fileName
already exists the search is skipped. If remote is true then the search is done remotely.
"""
if not os.path.isfile(fileName) or force:
output = open(fileName, "w")
command = [blastLocation + "/bin/blastp",
"-evalue", str(eValue),
"-outfmt", "5",
"-query", query]
if remote:
command += ["-remote",
"-db", database]
else:
command += ["-num_threads", str(Runtime.getRuntime().availableProcessors()),
"-db", database]
blastProcess = subprocess.Popen(command,
stdout = subprocess.PIPE)
while blastProcess.poll() == None:
output.write(blastProcess.stdout.read())
if pipeline.exception:
psProcess = subprocess.Popen(["ps", "aux"], stdout = subprocess.PIPE)
awkProcess = subprocess.Popen(["awk", "/" + " ".join(command).replace("/", "\\/") + "/"], stdin = psProcess.stdout, stdout = subprocess.PIPE)
for line in awkProcess.stdout:
subprocess.Popen(["kill", "-9", re.split(r"\s+", line)[1]])
output.close()
raise pipeline.exception
remaining = blastProcess.stdout.read()
while remaining:
output.write(remaining)
remaining = blastProcess.stdout.read()
output.close()
try:
return parseBlast(fileName)
except SAXParseException:
return cachedBlast(fileName, blastLocation, database, eValue, query, pipeline, remote, True)