##
## This is file `sagetex.py',
## generated with the docstrip utility.
##
## The original source files were:
##
## sagetex.dtx  (with options: `python')
## py-and-sty.dtx  (with options: `python')
## 
## This is a generated file. It is part of the SageTeX package.
## 
## Copyright (C) 2008--2011 by Dan Drake <ddrake@member.ams.org>
## 
## 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 2 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/>.
## 
__version__ = """
  [2011/05/27 v2.3.1 embedding Sage into LaTeX documents]
""".strip()
import sys
if __name__ == "__main__":
  print("""This file is part of the SageTeX package.
It is not meant to be called directly.

This file will be automatically used by Sage scripts generated from a
LaTeX document using the SageTeX package.""")
  sys.exit()
version = ' '.join(__version__.strip('[').split()[0:2])
from sage.misc.latex import latex
from sage.misc.preparser import preparse
import sage.plot.plot
sage.plot.plot.DOCTEST_MODE = True

import os
import os.path
import hashlib
import traceback
import subprocess
import shutil
import re
class SageTeXProcessor():
  def __init__(self, jobname):
    if ' ' in jobname:
      jobname = jobname.strip('"')
    self.progress('Processing Sage code for {0}.tex...'.format(jobname))
    self.didinitplot = False
    self.useimagemagick = False
    self.useepstopdf = False
    self.plotdir = 'sage-plots-for-' + jobname + '.tex'
    self.filename = jobname
    self.name = os.path.splitext(jobname)[0]
    autogenstr = """% This file was *autogenerated* from {0}.sagetex.sage with
 % sagetex.py version {1}\n""".format(self.name, version)
    self.max_inline_counter_seen = -1
    self.souttmp = open(self.filename + '.sagetex.sout.tmp', 'w')
    self.souttmp.write(autogenstr)
    self.scmdtmp = open(self.filename + '.sagetex.scmd.tmp', 'w')
    self.scmdtmp.write(autogenstr)
    self.scmdpos = 3
  def progress(self, t,linebreak=True):
    if linebreak:
      print(t)
    else:
      sys.stdout.write(t)
      sys.stdout.flush()
  def initplot(self):
    self.progress('Initializing plots directory')
    if os.path.isdir(self.plotdir):
      shutil.rmtree(self.plotdir)
    os.mkdir(self.plotdir)
    self.didinitplot = True
  def inline(self, counter, s, labelname=None):
      if counter == self.max_inline_counter_seen:
          return
      else:
          self.max_inline_counter_seen += 1
      if labelname is None:
          labelname = 'sageinline'
          self.progress('Inline formula {0}'.format(counter))
      elif labelname == 'sagecmdline' or labelname == 'sageexample':
          pass # output message already printed
      else:
          raise ValueError, 'inline() got a bad labelname'
      self.souttmp.write(r'\expandafter\def\csname ST@sout@' + labelname + str(counter) +
                         '\endcsname{%\n' + s.rstrip() + '}\n')
  def savecmd(self, counter, s):
      self.scmdtmp.write(s.rstrip() + "\n")
      begin = self.scmdpos
      end = begin + len(s.splitlines()) - 1
      self.scmdpos = end + 1
      return begin, end
  def blockbegin(self):
    self.progress('Code block begin...', False)
  def blockend(self):
    self.progress('end')
  def doctest(self, counter, str, globals, locals, output, input_style, output_style, execute_statement):
      # We strip away the empty pieces after the splitting to support ```output='''
      display = ['input'] + [piece for piece in re.split(' *, *', output) if piece]
      print 'in doctest'
      current_statement = None
      current_lines = None
      latex_string = ""
      line_iterator = (line.lstrip() for line in str.splitlines())

      # Gobbles everything until the first "sage: ..." block
      for line in line_iterator:
          if line.startswith("sage: "):
              break
      else:
          return
      sage_block = 0
      while True:
          # At each
          assert line.startswith("sage: ")
          statement  = line[6:]
          input = [line]
          output = []
          in_statement = True
          for line in line_iterator:
              if line.startswith("sage: "):
                  break
              in_statement = in_statement and line.startswith("...")
              if in_statement:
                  statement +="\n"+line[6:]
                  input.append(line)
              else:
                  output.append(line)
          else:
              line = None # we reached the last line
          # Now we have digested everything from the current ``sage: ...`` to the next one or to the last line
          # Strip away doctest annotations such as <BLANKLINE>, # long ...
          input  = [re.sub(" *# *long.*$", "", l) for l in input ]
          output = [re.sub("<BLANKLINE>$", "", l) for l in output]
          # Render this input/output
          result = None
          exception = False
          if execute_statement:
              statement = preparse(statement)
              try: # How to test whether the code is an Python expression or a statement?
                  # In the first case, we compute the result and include it in the latex
                  result = eval(statement, globals, locals)
              except Exception as e:
                  if isinstance(e, SyntaxError):
                      # We assume that the code was a statement, and just execute it
                      try:
                           exec statement in globals, locals
                      except Exception as e:
                           exception = True
                           result = e
                  else:
                      exception = True
                      result = e

          for piece in display:
              if piece == "latex" and not exception:
                  # Append the latex output in display math to the latex string
                  if result is not None:
                      latex_string += r"""\begin{displaymath}""" + "\n {0}\n".format(latex(result)) + r"\end{displaymath}" + "\n"
                  continue
              else:
                  # Append the relevant piece in verbatim mode to the latex string
                  if   piece == "input":
                      verbatim_lines = input
                      style = input_style
                      if any(verbatim_line.find('#@')>=0 for verbatim_line in verbatim_lines):
                          style+=",escapeinside={\\#@}{\\^^M}"
                  elif piece == "text" or piece == "draft text":
                      verbatim_lines = output
                      style = output_style
                  elif piece == "computed text" or piece == "latex":
                      if result is None:
                          verbatim_lines = []
                      else:
                          verbatim_lines = "{0}".format(result).splitlines()
                      style = output_style
                  else:
                      raise TypeError, "Unknown output option: %s"%piece
                  if verbatim_lines == []:
                      continue
                  begin, end = \
                  self.savecmd(counter, ''.join(verbatim_line+"\n"
                                                for verbatim_line in verbatim_lines))
                  filename = self.name + '.sagetex.scmd'
                  if ' ' in filename:
                      filename = '"' + filename + '"'
                  latex_string += r"\lstinputlisting[firstline={0},lastline={1},firstnumber={2},style={3}]{{{4}}}".format(begin, end, begin-2, style, filename)+"\n"
          input = output = statement = None
          if line is None: break
          sage_block += 1
      self.inline(counter, latex_string, labelname="sageexample")
  def commandline(self, counter, str, globals, locals, text_output):
      self.progress('Sage commandline {0}'.format(counter))
      current_statement = None
      current_lines = None
      line_iterator = (line.lstrip() for line in str.splitlines())
      latex_string = r"\vspace{\sagecommandlineskip}" + "\n"
      bottom_skip = ''

      # Gobbles everything until the first "sage: ..." block
      for line in line_iterator:
          if line.startswith("sage: "):
              break
      else:
          return
      sage_block = 0
      while True:
          # At each
          assert line.startswith("sage: ")
          current_statement  = line[6:]
          current_lines = line
          for line in line_iterator:
              if line.startswith("sage: "):
                  break
              elif line.startswith("...   "):
                  current_statement += "\n"+line[6:]
                  current_lines += "\n"+line
          else:
              line = None # we reached the last line
          # Now have everything from "sage:" to the next "sage:"

          if current_lines.find('#@')>=0:
              escapeoption = ',escapeinside={\\#@}{\\^^M}'
          else:
              escapeoption = ''

          begin, end = self.savecmd(counter,current_lines)
          filename = self.name + '.sagetex.scmd'
          if ' ' in filename:
              filename = '"' + filename + '"'
          latex_string += r"\lstinputlisting[firstline={0},lastline={1},firstnumber={2},style=SageInput{3}]{{{4}}}".format(begin, end, begin-2, escapeoption, filename)+"\n"

          current_statement = preparse(current_statement)
          try:  # is it an expression?
              result = eval(current_statement, globals, locals)
              resultstr = "{0}".format(result)
              begin, end = self.savecmd(counter,resultstr)
              if text_output:
                latex_string += r"\lstinputlisting[firstline={0},lastline={1},firstnumber={2},style=SageOutput]{{{3}}}".format(begin, end, begin-2, filename)+"\n"
                bottom_skip = r"\vspace{\sagecommandlineskip}" + "\n"
              else:
                latex_string += (
                  r"\begin{displaymath}" + "\n" +
                  latex(result)          + "\n" +
                  r"\end{displaymath}"   + "\n" )
                bottom_skip = ''
          except SyntaxError:  # must be a statement!
              exec current_statement in globals, locals
          current_lines = current_statement = None
          if line is None: break
          sage_block += 1
      latex_string += bottom_skip + r"\noindent" + "\n"
      self.inline(counter, latex_string, labelname='sagecmdline')
  def plot(self, counter, _p_, format='notprovided', **kwargs):
      if not self.didinitplot:
          self.initplot()
      self.progress('Plot {0}'.format(counter))
      if format == 'notprovided':
          formats = ['eps', 'pdf']
      else:
          formats = [format]
      for fmt in formats:
          if fmt == 'pdf' and self.useepstopdf:
              epsfile = os.path.join(self.plotdir, 'plot-{0}.eps'.format(counter))
              self.progress('Calling epstopdf to convert plot-{0}.eps to PDF'.format(
                            counter))
              subprocess.check_call(['epstopdf', epsfile])
              continue
          plotfilename = os.path.join(self.plotdir, 'plot-{0}.{1}'.format(counter, fmt))
          try:
              _p_.save(filename=plotfilename, **kwargs)
          except ValueError as inst:
              if 'filetype not supported by save' in str(inst):
                  newfilename = plotfilename[:-3] + 'png'
                  print '  saving {0} failed; saving to {1} instead.'.format(
                                                    plotfilename, newfilename)
                  _p_.save(filename=newfilename, **kwargs)
                  break
              else:
                  raise
          if format != 'notprovided' and self.useimagemagick:
              self.progress('Calling Imagemagick to convert plot-{0}.{1} to EPS'.format(
                counter, format))
              self.toeps(counter, format)
  def toeps(self, counter, ext):
    subprocess.check_call(['convert',\
      '{0}/plot-{1}.{2}'.format(self.plotdir, counter, ext), \
      '{0}/plot-{1}.eps'.format(self.plotdir, counter)])
  def goboom(self, line):
    print('\n**** Error in Sage code on line {0} of {1}.tex! Traceback\
 follows.'.format(line, self.filename))
    traceback.print_exc()
    print('\n**** Running Sage on {0}.sagetex.sage failed! Fix {0}.tex and try\
 again.'.format(self.filename))
    self.souttmp.close()
    os.remove(self.filename + '.sagetex.sout.tmp')
    self.scmdtmp.close()
    os.remove(self.filename + '.sagetex.scmd.tmp')
    sys.exit(int(1))
  def endofdoc(self):
    sagef = open(self.filename + '.sagetex.sage', 'r')
    m = hashlib.md5()
    for line in sagef:
      if line[0:12] != " _st_.goboom" and line[0:12] != "print 'SageT":
        m.update(line)
    s = '%' + m.hexdigest() + '% md5sum of corresponding .sage file\
 (minus "goboom" and pause/unpause lines)\n'
    self.souttmp.write(s)
    self.scmdtmp.write(s)
    self.souttmp.close()
    os.rename(self.filename + '.sagetex.sout.tmp', self.filename + '.sagetex.sout')
    self.scmdtmp.close()
    os.rename(self.filename + '.sagetex.scmd.tmp', self.filename + '.sagetex.scmd')
    self.progress('Sage processing complete. Run LaTeX on {0}.tex again.'.format(
             self.filename))

