#! /usr/bin/env python import sys import re import os import shutil from subprocess import Popen, PIPE, STDOUT, call from tempfile import mkstemp from optparse import OptionParser class FileFormat: PHYLIP, NEXUS, HENNIG86, UNKNOWN = range(4) DEFAULT_PAUP = "paup" class Logger: def debug(self, m): if options.noisy: print >>sys.stderr, m def warn(self, m): if not options.quiet: print >>sys.stderr, m _LOG = Logger() def diagnose_format(filename): f = open(filename, "rU") try: firstLine = f.readline() phylip_header = re.compile(r"^\s*\d+\s+\d+\s*$") if phylip_header.match(firstLine): _LOG.debug(filename + " appears to be PHYLIP.") return FileFormat.PHYLIP nexus_header = re.compile(r"^\#NEXUS", re.I) if nexus_header.match(firstLine): _LOG.debug(filename + " appears to be NEXUS.") return FileFormat.NEXUS hennig86_header = re.compile(r"^NSTATES|^XREAD", re.I) if hennig86_header.match(firstLine): _LOG.debug(filename + " appears to be Hennig86.") return FileFormat.HENNIG86 _LOG.debug(filename + " format could not be diagnosed.") return FileFormat.UNKNOWN finally: f.close() def phylip_to_nexus(filename): if options.phy_to_nex: _cipres_phylip_to_nexus(filename) else: _paup_phylip_to_nexus(filename) def _cipres_phylip_to_nexus(filename): _LOG.debug("Converting " + filename + "to NEXUS using phylip_to_nexus program.") toexec = options.phy_to_nex command = [toexec, "-indra", filename] if options.filename: outfn = options.filename command.append(os.path.abspath(outfn)) try: retcode = call(command, shell=False) except OSError: sys.exit("Conversion failed, make sure that a valid path to phylip_to_nexus is" " given (with a --phylip_to_nexus= option).\nTried to execute:\n" " %s" % " ".join(command)) if retcode < 0: print >>sys.stderr, "Child was terminated by signal", retcode else: print >>sys.stderr, "Child returned", retcode def _paup_phylip_to_nexus(filename): _LOG.debug("Converting " + filename + "to NEXUS using PAUP.") paup_command = options.paup or DEFAULT_PAUP shell = bool(options.paup is None) paup_proc = Popen(paup_command, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=True) if options.filename: outfn = options.filename else: outstream, outfn = mkstemp() try: try: o, e = paup_proc.communicate("ToNEXUS fromfile=%s format=phylip tofile=%s replace;\n" % (filename, outfn)) _LOG.debug(o) except OSError: sys.exit("Conversion failed, make sure that a valid path to paup is" " given (with a -p or --paup= option). \nTried to execute " "PAUP with the invocation \"%s\"." % paup_command) if not options.filename: nexus = open(outfn, "rU") try: c = nexus.read() sys.stdout.write(c) finally: nexus.close() finally: if not options.filename: _LOG.debug("removing " + outfn) os.remove(outfn) def unknown_to_nexus(filename): _LOG.debug("Treating unknown file " + filename + " as if it were NEXUS") nexus_to_nexus(filename) def nexus_to_nexus(filename): if options.filename: shutil.copyfile(filename, options.filename) else: nexus = open(filename, "rU") try: sys.stderr.write(nexus.read()) finally: nexus.close() def hennig86_to_nexus(filename): _LOG.debug("Converting " + filename + "to NEXUS using hennig86-to-nexus program.") toexec = options.hennig_to_nex command = [toexec, filename] if options.filename: outfn = options.filename command.append(os.path.abspath(outfn)) try: retcode = call(command, shell=False) except OSError: sys.exit("Conversion failed, make sure that a valid path to hennig86-to-nexus is" " given (with a --hennig86-to-nexus= option).\nTried to execute:\n" " %s" % " ".join(command)) if retcode < 0: print >>sys.stderr, "Child was terminated by signal", retcode else: print >>sys.stderr, "Child returned", retcode parser = OptionParser() parser.add_option("-f", "--file", dest="filename", help="NEXUS file to write", metavar="FILE") parser.add_option("", "--phylip-to-nexus", dest="phy_to_nex", help="path to CIPRES' phylip_to_nexus tool", metavar="FILE") parser.add_option("-p", "--paup", dest="paup", help="path to PAUP -- required if PAUP* is not on you path as \"%s\" and you wish to perform some conversions (e.g. a PHYLIP to NEXUS conversion)" % DEFAULT_PAUP, metavar="FILE") parser.add_option("", "--hennig86-to-nexus", dest="hennig_to_nex", help="path to CIPRES' hennig86-to-nexus tool", metavar="FILE") parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="suppress warnings") parser.add_option("-n", "--noisy", action="store_true", dest="noisy", default=False, help="print status messages to stderr") (options, args) = parser.parse_args() if len(args) != 1: sys.exit("Expecting a file to convert to NEXUS") if options.filename and os.path.exists(options.filename): _LOG.warn(options.filename + " will be replaced") converter = { FileFormat.PHYLIP : phylip_to_nexus, FileFormat.NEXUS : nexus_to_nexus, FileFormat.HENNIG86: hennig86_to_nexus, FileFormat.UNKNOWN : unknown_to_nexus, } for infile_name in args: format = diagnose_format(infile_name) convFunc = converter[format] convFunc(infile_name)