diff --git a/PythonLatex/__init__.py b/PythonLatex/__init__.py new file mode 100644 index 0000000..1bc461b --- /dev/null +++ b/PythonLatex/__init__.py @@ -0,0 +1 @@ +__all__ = ["latex_structure", "generate_latex"] diff --git a/PythonLatex/article.header b/PythonLatex/article.header new file mode 100644 index 0000000..c37d3db --- /dev/null +++ b/PythonLatex/article.header @@ -0,0 +1,30 @@ +\documentclass[10pt]{article} + +\usepackage[T1]{fontenc} +\usepackage[latin1]{inputenc} +\usepackage{color,graphicx} +\usepackage{url} +\usepackage{xifthen} +\usepackage{wrapfig} +\usepackage{fullpage} +\usepackage{amssymb,amsmath,amsthm} +\usepackage{amsthm} +\usepackage{verbatim} + +\newenvironment{python}[1]{ +\vspace{0.5cm} +\hrule +\vspace{0.2cm} +\noindent Block: #1 +\verbatim +}{ +\endverbatim +} + +\newcommand{\py}[1]{ +\emph{\color{red}{PythonBlock:#1}} +} + +\renewcommand{\vec}[1]{ +\mathbf{#1} +} diff --git a/PythonLatex/beamer.header b/PythonLatex/beamer.header new file mode 100644 index 0000000..6895750 --- /dev/null +++ b/PythonLatex/beamer.header @@ -0,0 +1,30 @@ +\documentclass[9pt,xcolor=dvipsnames]{beamer} + + +\usepackage[T1]{fontenc} +\usepackage[latin1]{inputenc} +\usepackage{color,graphicx} +\usepackage{url} +\usepackage{xifthen} +\usepackage{wrapfig} +\usepackage{amssymb,amsmath,amsthm} +\usepackage{amsthm} +\usepackage{verbatim} + +\newenvironment{python}[1]{ +\vspace{0.5cm} +\hrule +\vspace{0.2cm} +\noindent Block: #1 +\verbatim +}{ +\endverbatim +} + +\newcommand{\py}[1]{ +\emph{\color{red}{PythonBlock:#1}} +} + +\renewcommand{\vec}[1]{ +\mathbf{#1} +} diff --git a/PythonLatex/generate_latex.py b/PythonLatex/generate_latex.py new file mode 100644 index 0000000..8fcb602 --- /dev/null +++ b/PythonLatex/generate_latex.py @@ -0,0 +1,344 @@ +#! /usr/bin/python + +import re as myre +import subprocess +import PythonLatex.latex_structure as tex +import matplotlib.pyplot as plt +import types +import sympy as sym +import hashlib,dill + +################################################################ +from sympy.printing.latex import LatexPrinter +from sympy import latex +from sympy.core.function import Derivative + +class CustomStrPrinter(LatexPrinter): + + def nbIdenticalDx(self,expr): + first_dx = expr.args[1:][0] + count = 0 + for dx in expr.args[1:]: + if dx == first_dx: + count += 1 + if count == len(expr.args[1:]): + return count,first_dx + + return 0,None + + def _print_Derivative(self, expr): +# print expr +# print LatexPrinter._print_Derivative(self,expr) + + count,dx = self.nbIdenticalDx(expr) + f = expr.args[0] + if len(f.args) == 0: + return LatexPrinter._print_Derivative(self,expr) + + dxf = f.args[0] + + + f_nargs = len(f.args) + # case where ' applies + if f_nargs == 1 and dx == dxf: + return latex(expr.args[0].func) + "'"*len(expr.args[1:]) + "(" + latex(dxf) + ")" + + return LatexPrinter._print_Derivative(self,expr) + + def _print_Subs(self,expr): + if not isinstance(expr.args[0],Derivative): + return LatexPrinter._print_Subs(self,expr) + +# print expr + deriv = expr.args[0] + _dxs = deriv.args[1:] +# print _dxs + p1 = expr.args[1] + p2 = expr.args[2] +# print len(_dxs) + + _subst = {} + _subst2 = {} + for i in range(0,len(p1)): + _subst[p1[i]] = p2[i] +# + if hasattr(p2[i],'args'): + _p2 = type(p2[i]) + _subst2[p1[i]] = _p2 + + _dxs = [e.subs(_subst2) for e in _dxs ] + + new_f = deriv.args[0].subs(_subst) +# print 'CCCCCCCCC' +# print new_f +# print _dxs +# print _subst + + tmp = Derivative(new_f,*_dxs) + + +# print tmp + return latex(tmp) +# return LatexPrinter._print_Subs(self,expr) + +################################################################ + +class PyCode(tex.LatexBlock): + """ + """ + + def checkLine(self,line): + + m = myre.match(r"\\end{python}",line) + if m: + return Block() + + return Block.checkLine(self,line) + + + def checkValidity(self): + for c in self.content: + if type(c) == types.InstanceType: + raise Exception("python block cannot have subblocks") + + def __str__(self): + return "" + + def generateHashSource(self): + txt_content = self.content[0] + self.hash_source = hashlib.sha1(txt_content).hexdigest() + gl = globals() + self.global_keys_before = gl.keys() + self.local_keys_before = self.__dict__.keys() + + def needReexecute(self): + if self.alias == 'header': return True + if 'loaded_hash_source' in self.__dict__: + return not self.loaded_hash_source == self.hash_source + return True + + + def saveBlockOutputInfo(self): + if self.alias == 'header': return + global_keys = globals().keys() + global_keys_to_save = set(global_keys) - set(self.global_keys_before) + global_keys_to_save = list(global_keys_to_save) + global_entries_to_save = dict([(e,globals()[e]) for e in global_keys_to_save]) + + local_keys = self.__dict__.keys() + local_keys_to_save = set(local_keys) - set(self.local_keys_before) + local_keys_to_save = list(local_keys_to_save) + local_entries_to_save = dict([(e,self.__dict__[e]) for e in local_keys_to_save]) + + + try: + for k in global_entries_to_save: + try: + print 'try to save ',k,' ',latex(global_entries_to_save[k]) + global_entries_to_save[k] = dill.dumps(global_entries_to_save[k]) + print 'saved ',k + except Exception as e: + print e + + self.global_block_result = dill.dumps(global_entries_to_save) + self.local_block_result = dill.dumps(local_entries_to_save) + self.global_hash_result = hashlib.sha1(self.global_block_result).hexdigest() + self.local_hash_result = hashlib.sha1(self.local_block_result).hexdigest() + except Exception as e: + print "cannot save block: {0}\n{1}".format(self.alias,e) + self.global_block_result = None + self.global_hash_result = None + self.local_block_result = None + self.local_hash_result = None + self.hash_source = None + + filename = self.alias + '.pytex.out' + f = open(filename,'w') +# print "save symbols {0}: hash_source {1}".format(self.alias,self.hash_source) + raw_save = [self.hash_source,self.global_hash_result,self.global_block_result,self.local_hash_result,self.local_block_result] + dill.dump(raw_save,f) + f.close() + + + + def loadBlockOutputInfo(self): + return + if self.alias == 'header': return + filename = self.alias + '.pytex.out' + try: + f = open(filename,'r') + self.loaded_hash_source,self.global_hash_result,self.global_block_result,self.local_hash_result,self.local_block_result = dill.load(f) +# print "loaded symbols" + #print "AAAAAAAAAAA " + str(self.loaded_hash_source) + globals().update(dill.loads(self.global_block_result)) + self.__dict__.update(dill.loads(self.local_block_result)) +# print pickle.loads(self.global_block_result).keys() +# print self.__dict__.keys() + f.close() + print 'loaded {0}'.format(filename) + except Exception as e: +# print "Could not load block " + self.alias +# print e + pass + + + def evalBlock(self): + txt_content = self.content[0] + self.generateHashSource() + self.loadBlockOutputInfo() + need_reexecute = self.needReexecute() + if need_reexecute or reexec: + print "execute block: {0}".format(self.alias) + gl = globals() + gl.update({'self':self}) + try: + exec(txt_content,gl) + except Exception as e: + mesg = """For block '{0}' there was an execution problem +{1} +Block was: +************ +{2} +""".format(self.alias,e,txt_content) + if (not continue_flag): raise Exception(mesg) + else: print (mesg) + + del gl['self'] + self.saveBlockOutputInfo() + + + def getContent(self,varname): + try: + c = self.__dict__[varname] + except Exception as e: + raise Exception("cannot retreive variable " + varname + " from block " + self.alias) + + if (myre.search('sympy',str(type(c)))): + c = sym.latex(c) + return c + + + def __init__(self,name,parent,options): + + tex.LatexBlock.__init__(self,name,parent,options) + if not self.name == 'python': + raise Exception('cannot create a PyCode block from block type ' + self.name) + if self.options is None: + raise Exception("not valid python block") + m = myre.match(r"{(.*?)}",self.options) + if not m: + raise Exception("not valid options " + self.options) + self.alias = m.group(1) + self.block_eval = None + + +################################################################ + +def evaluatePythonBlock(b): + if b.name == 'python': + globals()[b.alias] = b + b.evalBlock() + +def replaceAliases(b,text): + + if b.name == 'python': + return text + + if type(text) == str: +# print text + regex = r'\\py{(.*?)}' + m = myre.findall(regex,text) + if not m: + return text +# print text + for expression in m: +# print expression + try: + exec("ans = " + expression) + except Exception as e: + print "Block " + str(b.name) + " Cannot parse expression '" + str(expression) + "'\n" + str(e) + + rep = '\\py{' + expression + '}' + + try: + ans_str = str(latex(ans)) + + except Exception as e: + print "AAAAAAAA " + str(e) + return text + +# print "ans is now " + str(ans) + globals().update({'ans':ans}) + # print "replace " + rep + " with " + cont + + text = text.replace(rep,ans_str,1) + #print text + return text + + + +################################################################ + +def collectScript(b,text): + if b.name == 'python': + globals()['total_python_script'] += "{0} = CodeEval()".format(b.alias) + globals()['total_python_script'] += text.replace('self',b.alias) + return text + + if type(text) == str: +# print text + regex = r'\\py{(.*?)}' + m = myre.findall(regex,text) + if not m: + return text + # print m + for expression in m: + globals()['total_python_script'] += "print 'printing expression: {0}'\n".format(str(expression).replace("'","\\'")) + globals()['total_python_script'] += "ans = " + expression + "\n" + globals()['total_python_script'] += "print str(latex(ans)) + '\\n\\n'\n" + return text + + + + +################################################################ +continue_flag = False +reexec = True + +def interpretPython(filename,c_flag, reexec_flag): + + continue_flag = c_flag + reexec = reexec_flag + tex_struct = tex.LatexStructure() + + tex_struct.buildLatexBlocks(filename,{'python':PyCode}) + + total_python_script = """ +from PythonLatex.generate_latex import * +class CodeEval: + pass + +""" + globals().update({'total_python_script':total_python_script}) + globals().update({'reexec':reexec}) + globals().update({'continue_flag':continue_flag}) + + + tex_struct.pathInBlock(ftext=collectScript) + tex_struct.pathInBlock(fbegin=evaluatePythonBlock,ftext=replaceAliases) + + + total_python_script = globals()['total_python_script'] + +# +# pyblocks = tex_struct.getBlocksFromType("python") +# +# for b in pyblocks: +# total_python_script += b.content[0] +# + + tmp_file = open('python_script.py','w') + tmp_file.write(total_python_script) + tmp_file.close() + + return str(tex_struct) diff --git a/PythonLatex/generic_headers.py b/PythonLatex/generic_headers.py new file mode 100644 index 0000000..a4c54b1 --- /dev/null +++ b/PythonLatex/generic_headers.py @@ -0,0 +1,85 @@ +#! /usr/bin/python + +import matplotlib.pyplot as plt + +################################################################ +def generateFigure(fig,name,caption=None,width=1): + + if type(fig) is not list: fig = [fig] + if type(name) is not list: name = [name] + if type(width) is not list: width = [width] + + res = '{\\center' + for f,n,w in zip(fig,name,width): + f.savefig(n + ".png") + + if type(w) == float or type(w) == int: + w = str(w) + "\\textwidth" + + res += "\\includegraphics" + "[width=" + w + "]{" + n + ".png}" + + res += '}' + if caption is not None: + res += "\\caption{" + res += caption + res += "}" + return res + +################################################################ + +def make2Dplot(np_x,np_y,fig=None,axe=None,label=None,xlabel=None,ylabel=None,loglog_flag=False,**kwargs): + + if fig is None: fig = plt.figure() + if axe is None: axe = fig.add_subplot(1,1,1) + + if xlabel is not None: axe.set_xlabel(xlabel) + if ylabel is not None: axe.set_ylabel(ylabel) + + if loglog_flag is False: _plot = axe.plot + if loglog_flag is True: _plot = axe.loglog + + if label is not None: + _plot(np_x,np_y,label=label,**kwargs) + axe.legend(loc='best') + else: _plot(np_x,np_y,**kwargs) + + return fig,axe + +################################################################ +def generateArticle(): + import os + path = os.path.dirname(__file__) + f = open(os.path.join(path,'./article.header')) + return f.read() + +################################################################ + +def generateBeamer(): + import os + path = os.path.dirname(__file__) + f = open(os.path.join(path,'./beamer.header')) + return f.read() + +flags = {} +flags['slide_open'] = False + +def closeSlide(): + flags['slide_open'] = False + return "\\end{frame}\n" + + +def newSlide(title,subtitle=None): + res = "" + if flags['slide_open']: res += closeSlide() + + flags['slide_open'] = True + res += """\\begin{frame} +\\frametitle{""" + '{0}'.format(title) + "}\n" + if subtitle is not None: + res += """\\framesubtitle{""" + '{0}'.format(subtitle) + '}\n\n' + return res + + + + + diff --git a/PythonLatex/latex_structure.py b/PythonLatex/latex_structure.py new file mode 100644 index 0000000..db9c4b4 --- /dev/null +++ b/PythonLatex/latex_structure.py @@ -0,0 +1,156 @@ +#!/usr/bin/python + +import re +import types +################################################################ +class LatexBlock: + + def createSubBlock(self,name,options, herited_types): + + btype = LatexBlock + + if name in herited_types: + btype = herited_types[name] + + new_block = btype(name,self,options) + self.content.append(new_block) + return new_block + + + + def endSubBlock(self,name): + if not name == self.name: + raise Exception('Problem ' + name) + + new_content = [] + for c in self.content: + + if len(new_content) and type(c) == str and type(new_content[-1]) == str: + new_content[-1] += c + else: + new_content.append(c) + + self.content = new_content +# print self.content + self.checkValidity() + return self.parent + + def appendContent(self,txt): + self.content.append(txt) + return self + + + def __str__(self): + res = "" + if self.name: + res += r"\begin{" + self.name + "}" + if self.options: + res += self.options + for c in self.content: + res += str(c) + + if self.name: + res += r"\end{" + self.name + "}" + return res + + def checkValidity(self): + pass + + def __init__(self,name=None,parent=None,options=None): + self.content = [] + self.name = name + self.parent = parent + self.options = options +################################################################ + +class LatexStructure: + + def getBlocksFromType(self,typ): + mylist = [] + def foo(b): + if b.name == typ: + mylist.append(b) + + self.pathInBlock(fbegin=foo) + return mylist + + def buildLatexBlocks(self,filename,herited_types=dict()): + fin = open(filename,'r') + inp = fin.readlines() + inp = "".join(inp) + latex_cmd_expr = r'(\\\w+(?:\[\w*\])*(?:{[\w|,|\.|(|)]*?})+)' + splitted = re.split(latex_cmd_expr,inp) + self.main_block = LatexBlock() + self.current_block = self.main_block + + for i in splitted: + m = re.match(r'\\begin{(.*?)}(.*)',i) + if m: + name = m.group(1) + options = m.group(2) + self.current_block = self.current_block.createSubBlock(name,options,herited_types) + continue + + m = re.match(r'\\end{(.*?)}',i) + if m: + name = m.group(1) + try: + self.current_block = self.current_block.endSubBlock(name) + except Exception as e: + print "AAAAAAAAAAAAAAAAAA" + print e + continue + + self.current_block.appendContent(i) + + + if (not self.current_block == self.main_block): + raise Exception("one latex block was not closed: {0}".format(self.current_block.name)) + + + + def pathInBlock(self,block=None,fbegin=None,fend=None, ftext=None): + + if block is None: + block = self.main_block + + if fbegin is not None: + fbegin(block) + + + for i in range(len(block.content)): + c = block.content[i] + if type(c) == types.InstanceType: + try: + self.pathInBlock(c,fbegin,fend,ftext) + except Exception as e: + print e + + else: + if ftext is not None: + block.content[i] = ftext(block,c) + if fend is not None: + fend(block) + + + def __str__(self): + return str(self.main_block) + + + def __init__(self): + + self.main_block = None + self.current_block = None + +################################################################ + +if __name__ == '__main__': + import sys + + filename_in = sys.argv[1] + filename_out = sys.argv[2] + + latex_blocks = buildLatexBlocks(filename_in) + fout = open(filename_out,'w') + + fout.write(str(latex_blocks))