I have written a small (and buggy) python module to drive latex. This can be used in larger programs (eg taylortype https://launchpad.net/taylortype)

All it does is execute the latex command, read the output and return a nicely formatted error message if something goes wrong.

Here is the code:

http://dl.dropbox.com/u/3746044/python-latex.py

http://www.pasteall.org/11691/python

#!/usr/bin/env python

##Copyright (C) 2010 Louis Taylor  
## _    ___ _____                                                              
##| |  | _ \_   _|                                                             
##| |__|  _/ | |                                                               
##|____|_|   |_| *programus optimus est*                                       
##                                                                             
##This program is free software: you can redistribute it and/or modify         
##it under the terms of the GNU General Public License as published by         
##the Free Software Foundation, either version 3 of the License, or            
##(at your option) any later version.                                          
##                                                                             
##This program is distributed in the hope that it will be useful,              
##but WITHOUT ANY WARRANTY; without even the implied warranty of               
##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                
##GNU General Public License for more details.                                 
##                                                                            
##You should have received a copy of the GNU General Public License            
##along with this program.  If not, see http://www.gnu.org/licenses/gpl-3.0.txt  

import os, subprocess, time

def compile(latexfile, pdfdir='/tmp', rmlog=False, movepdf=False, usebibtex=True):
    '''compiles latex document, saves it to the directory
    specified in *pdfdir* and returns any error messages it can find'''
    path = os.path.split(latexfile)
    texfile = path[1]
    directory = path[0]

    rawname = os.path.splitext(texfile)[0]

    logfile = os.path.splitext(texfile)[0]+'.log'
    logpath = os.path.join(pdfdir, logfile)

    pdffile = os.path.splitext(texfile)[0]+'.pdf'
    pdfpath = os.path.join(pdfdir, pdffile)

    os.chdir(directory) #change cwd to the dir to place all latex files

    #os.system('bibtex %s' % rawname) #this will be replaced by the new function
    bibtex_error = False
    if usebibtex == True:
        print 'using bibtex'
        bib_err = bibtex(rawname, pdfdir)
        if bib_err != '':
            #get an error message
            bibtex_error = ['error', 'BibTeX error', '', bib_err]
        else:
            pass

    else:
        print 'not using bibtex'

    cmd = ['pdflatex', '--halt-on-error', '-output-directory', pdfdir, texfile]
    latex = subprocess.Popen(cmd, shell=False,
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    message = latex.communicate()[0] #get the stdout, latex.communicate()[1] gives stderr
    #set the data to be returned on success
    returned = ['success', pdfpath]

    if '\n!' in message:
        name = False
        ln = False
        error_msg = ''
        error_ln = ''
        quote = ''

        for line in message.split('\n'):
            if line == '':
                pass #pass if the line is blank

            elif line[0] == '!' and not name:
                #find error message line
                name = True
                error_msg = line.replace('!', '').strip()
                                        #remove all whitespace around string

            elif line[0] == 'l' and name and not ln:
                #find line number and quote.
                #only come to this line if:
                #the-- first character is an 'l', there has just been an error
                #line and there has not been a line number before.
                ln = True
                string = line.split(' ')
                error_ln = string[0].replace('l.', '')
                quote = line.replace(string[0], '')

            else:
                pass

    #repeat the BibTeX command, as requested by Peter Flynn
    if usebibtex == True:
        print 'using bibtex'
        bib_err = bibtex(rawname, pdfdir)
        if bib_err != '':
            #get an error message
            bibtex_error = ['error', 'BibTeX error', '', bib_err]
        else:
            pass

    else:
        print 'not using bibtex'

        #set the data to be returned on error
        try:
            returned = ['error', error_msg, error_ln, quote, pdffile]
        except UnboundLocalError:
            returned = ['success', pdfpath]

    if rmlog:
        try:
            os.remove(logpath)
        except OSError:
            pass

    if movepdf != False:
        #bit of a quick hack, use cp to do the work:
            #use python to do this?
        os.system('cp "%s" "%s"' % (pdfpath, movepdf))

    if returned == ['success', pdfpath] and bibtex_error == False:
        '''return normal traceback if everything whent ok'''
        return returned
    if returned != ['success', pdfpath] and bibtex_error == False:
        '''return latex error message'''
        return returned
    if returned == ['success', pdfpath] and bibtex_error != False:
        '''return bibtex error'''
        return bibtex_error
    if returned != ['success', pdfpath] and bibtex_error != False:
        '''let the latex error have preference over bibtex error'''
        return returned

def bibtex(rawtexname, directory):
    #os.chdir(directory)

    cmd = ['bibtex', rawtexname]
    bibtex = subprocess.Popen(cmd, shell=False,
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    message = bibtex.communicate()[0] #get the stdout, bibtex.communicate()[1] gives stderr
    print message

    error = ''
    for line in message.split('\n'):
        if error != '':
            #if we already have the first bibtex error message
            break
        else:
            #if we do not have an error message yet
            words = line.split()
            if line == '':
                break
            elif line[0] == 'I':
                #if line is one of the variations on "I couldn't..."
                if words[1] == 'found':
                    error = line.replace('I', '').replace('---', ' ').strip()
                elif words[1] == "couldn't":
                    error = line.replace('I', '').strip()

            elif 'Warning--' in line:
                error = line.replace('Warning--I ', '')
    return error

if __name__ == '__main__':
    #small self test
    error = compile('/home/louis/Desktop/tex/text.tex', '/home/louis/Desktop/tex', True, movepdf=False, usebibtex=True)
    print error
Advertisements