All:

Attached is a TeXmacs file that documents the latest efforts of the required modifications to the existing Python plugin required to adapt this plugin to a dedicated SymPy plugin. Also attached is the existing Python plugin file tm_python which I have modified to accommodate SymPy LaTeX output to TeXmacs.

It might also be possible to use what I have done so far and merely modify the existing Python plugin to accommodate SymPy LaTeX output which is what I have accomplished here while preserving as much of the original Python plugin code as possible.

TeXmacs user Bill Eaton first brought the attention of users to this issue and several replies seemed to favor some accommodation for his goal. Personally I am not sure I will ever use this feature if it is brought to fruition, but the challenge sort of got under my skin, so I gave it a go and the results of what I was able to accomplish so far are attached. There are probably some sharp Pythonists out there that can see this through from here. Sorry, but I am not in that category, as my Python programming skills are not that sharp.

My input to those that follow is that the best solution is to modify the existing Python plugin instead of creating a new one dedicated to SymPy. If someone can figure out from what I have done how to distinguish SymPy LaTeX text string objects and plain old Python text string objects when the output to TeXmacs is composed, then I think this is all that stands in the way. This is clear if you read the attached TeXmacs file with the included Python/SymPy session. The output is consistent with what we expect when using TeXmacs.

David Miller



<TeXmacs|1.0.7.15>

<style|generic>

<\body>
  UPDATED: 19 May 2013

  \;

  From: David E. Miller

  Subject: Modifying <name|Python> plugin for feasibility of <name|SymPy>
  plugin.

  \;

  In the <TeXmacs> <name|Python> plugin file t<verbatim|m_python>:

  Add to plugin file t<verbatim|m_python> import statement to import
  <verbatim|sympy latex> method:

  <\framed>
    <\verbatim-code>
      \;

      import os

      import traceback

      import keyword

      import re

      import string

      from sympy import latex # added to import sympy latex method

      DATA_BEGIN = chr(2)

      DATA_END = chr(5)

      DATA_ESCAPE = chr(27)

      DATA_COMMAND = chr(16)

      \;
    </verbatim-code>
  </framed>

  <\warning*>
    If <name|SymPy> module is not accessible, then an error is likely to be
    returned here. Probably need to add a condition that checks for the
    module and returns a ``nice'' message that <name|SymPy> module is not
    installed or not accessible.
  </warning*>

  Edit/replace the <verbatim|compose_out(data)> method as follows

  <\framed>
    <\verbatim-code>
      \;

      def compose_output(data):

      \ \ \ \ \ """Do some parsing on the output according to its type."""

      \ \ \ \ \ if isinstance(data, str) and data == "":

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return 'verbatim: %s' % str(data)

      \ \ \ \ \ if isinstance(data, str) and data.find('Traceback') != -1:

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return 'verbatim: %s' % str(data)

      \ \ \ \ \ if isinstance(data, str) and data.find('Error:') != -1:

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return 'verbatim: %s' % str(data)

      \ \ \ \ \ if isinstance(data, int):

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return 'verbatim: %d' % data

      \ \ \ \ \ if isinstance(data, float):

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return 'verbatim: %f' % data

      \ \ \ \ \ if isinstance(data, unicode):

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ data2=r''

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ for c in data:

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ if c not in
      string.printable:

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 
data2+='\\x%x'
      % ord(c)

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ else:

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ data2+=c

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ data=data2

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return 'verbatim: %s' % str(data)

      \ \ \ \ \ \ data = latex(data,mode='inline')

      \ \ \ \ \ \ return 'latex: %s' % str(data)

      \;
    </verbatim-code>
  </framed>

  <\note*>
    The <verbatim|latex> method uses the <verbatim|inline> mode when used in
    this way.
  </note*>

  Then a <TeXmacs> <name|Python> session will return <LaTeX> formatted output
  as shown below:

  <\session|python|default>
    <\output>
      Python plugin for TeXmacs.

      Please see the documentation in Help -\<gtr\> plugins -\<gtr\> Python
    </output>

    <\input|Python] >
      from __future__ import division
    </input>

    <\input|Python] >
      from sympy import *
    </input>

    <\input|Python] >
      x, y, z, t = symbols('x y z t')
    </input>

    <\input|Python] >
      k, m, n = symbols('k m n', integer=True)
    </input>

    <\input|Python] >
      f, g, h = symbols('f g h', cls=Function)
    </input>

    <\input|Python] >
      from sympy import Integral, latex
    </input>

    <\input|Python] >
      from sympy.abc import x
    </input>

    <\unfolded-io|Python] >
      189856
    <|unfolded-io>
      \ 189856
    </unfolded-io>

    <\unfolded-io|Python] >
      3.14159
    <|unfolded-io>
      \ 3.141590
    </unfolded-io>

    <\unfolded-io|Python] >
      "a string"
    <|unfolded-io>
      <math|a*s*t*r*i*n*g>
    </unfolded-io>

    <\textput>
      This looks like a problem. Strings are being formatted as <LaTeX>
      variables.

      \;
    </textput>

    <\unfolded-io|Python] >
      \\text{a string}
    <|unfolded-io>
      \ Traceback (most recent call last):

      \ \ File "tm_python", line 1

      \ \ \ \ \\text{a string}

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ^

      SyntaxError: unexpected character after line continuation character
    </unfolded-io>

    <\textput>
      This does not work either. <name|Python> system error messages are also
      affected. So I added some code to catch exceptions in
      <verbatim|compose_out> method to return exception and error messages
      verbatim. Otherwise these would be returned as <LaTeX> and these
      messages do not display well if not verbatim. The question that remains
      if you want to output strings is how to detect what is <LaTeX> and what
      is not. What is not (non-<LaTeX> strings), needs to be returned
      <verbatim|verbatim> formatted as a string. It looks like everything
      else is okay. Or you should merely not use strings with <name|SymPy>.
    </textput>

    <\textput>
      \;
    </textput>

    <\unfolded-io|Python] >
      while True print 'Hello world'
    <|unfolded-io>
      \ Traceback (most recent call last):

      \ \ File "tm_python", line 1

      \ \ \ \ while True print 'Hello world'

      \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ^

      SyntaxError: invalid syntax
    </unfolded-io>

    <\textput>
      Same issue here, but the error type is different. Again some code was
      added to catch this error and format it as <verbatim|verbatim:> instead
      of <verbatim|latex:> to avoid the display issue.

      \;
    </textput>

    <\unfolded-io|Python] >
      x**2
    <|unfolded-io>
      <math|x<rsup|2>>
    </unfolded-io>

    <\unfolded-io|Python] >
      1/x
    <|unfolded-io>
      <math|<frac|1|x>>
    </unfolded-io>

    <\unfolded-io|Python] >
      Integral(x**2, x)
    <|unfolded-io>
      <math|<big|int>x<rsup|2>*<space|0.25spc>d*x>
    </unfolded-io>

    \;

    <\unfolded-io|Python] >
      (1/cos(x)).series(x, 0, 10)
    <|unfolded-io>
      
<math|1+<frac|1|2>*x<rsup|2>+<frac|5|24>*x<rsup|4>+<frac|61|720>*x<rsup|6>+<frac|277|8064>*x<rsup|8>+<with|math-font-family|rm|math-font|cal|O><around*|(|x<rsup|10>|)>>
    </unfolded-io>

    <\input|Python] >
      from sympy import Rational
    </input>

    <\input|Python] >
      a = Rational(1,2)
    </input>

    <\unfolded-io|Python] >
      a
    <|unfolded-io>
      <math|<frac|1|2>>
    </unfolded-io>

    <\unfolded-io|Python] >
      a**2
    <|unfolded-io>
      <math|<frac|1|4>>
    </unfolded-io>

    <\unfolded-io|Python] >
      Rational(2)**50/Rational(10)**50
    <|unfolded-io>
      <math|<frac|1|88817841970012523233890533447265625>>
    </unfolded-io>

    <\unfolded-io|Python] >
      1/2
    <|unfolded-io>
      \ 0
    </unfolded-io>

    <\unfolded-io|Python] >
      \ x+y+x-y
    <|unfolded-io>
      <math|2*x>
    </unfolded-io>

    <\unfolded-io|Python] >
      (x+y)**2
    <|unfolded-io>
      <math|<around*|(|x+y|)><rsup|2>>
    </unfolded-io>

    <\unfolded-io|Python] >
      ((x+y)**2).expand()
    <|unfolded-io>
      <math|x<rsup|2>+2*x*y+y<rsup|2>>
    </unfolded-io>

    <\input|Python] >
      from sympy import apart
    </input>

    <\unfolded-io|Python] >
      1/( (x+2)*(x+1) )
    <|unfolded-io>
      <math|<frac|1|<around*|(|x+1|)>*<around*|(|x+2|)>>>
    </unfolded-io>

    <\unfolded-io|Python] >
      apart(1/( (x+2)*(x+1) ), x)
    <|unfolded-io>
      <math|-<frac|1|x+2>+<frac|1|x+1>>
    </unfolded-io>

    <\input|Python] >
      from sympy import limit, Symbol, sin, oo
    </input>

    <\input|Python] >
      x = Symbol("x")
    </input>

    <\unfolded-io|Python] >
      limit(sin(x)/x, x, 0)
    <|unfolded-io>
      <math|1>
    </unfolded-io>

    <\input|Python] >
      from sympy import diff, Symbol, sin, tan
    </input>

    <\unfolded-io|Python] >
      diff(sin(x), x)
    <|unfolded-io>
      <math|<math-up|cos><around*|(|x|)>>
    </unfolded-io>

    <\unfolded-io|Python] >
      diff(sin(2*x), x)
    <|unfolded-io>
      <math|2<math-up|cos><around*|(|2*x|)>>
    </unfolded-io>

    <\unfolded-io|Python] >
      diff(tan(x), x)
    <|unfolded-io>
      <math|<math-up|tan><rsup|2><around*|(|x|)>+1>
    </unfolded-io>

    <\input|Python] >
      from sympy import Matrix
    </input>

    <\unfolded-io|Python] >
      Matrix([[1,0], [0,1]])
    <|unfolded-io>
      
<math|<around*|(|<matrix*|<tformat|<table|<row|<cell|1>|<cell|0>>|<row|<cell|0>|<cell|1>>>>>|)>>
    </unfolded-io>

    <\unfolded-io|Python] >
      Matrix([[1,0,3], [5,4,3],[0,1,8]])
    <|unfolded-io>
      
<math|<around*|(|<matrix*|<tformat|<table|<row|<cell|1>|<cell|0>|<cell|3>>|<row|<cell|5>|<cell|4>|<cell|3>>|<row|<cell|0>|<cell|1>|<cell|8>>>>>|)>>
    </unfolded-io>

    \;

    <\input|Python] >
      \;
    </input>
  </session>

  This is as much code as I have tried using this latest version.
</body>

<\initial>
  <\collection>
    <associate|language|american>
    <associate|page-type|letter>
  </collection>
</initial>
#!/usr/bin/env python
#####################################################################
##
## MODULE      : tm_python.scm
## DESCRIPTION : Initialize python plugin
## COPYRIGHT   : (C) 2004  Ero Carrera, e...@dkbza.org
##               (C) 2012  Adrian Soto
## This software falls under the GNU general public license version 3 or later.
## It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
## in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.

import os
import traceback
import keyword
import re
import string
from sympy import latex # added to import sympy latex method
DATA_BEGIN = chr(2)
DATA_END = chr(5)
DATA_ESCAPE = chr(27)
DATA_COMMAND = chr(16)

__version__='1.0'
__author__='Ero Carrera, Adrian Soto'

class Capture:
        """Capture python output.
        
        Class in charge of recording the output of the
        statements/expressions entered in the TeXmacs
        session and executed in Python.
        """
        def __init__(self):
                self.text = ''
        def write(self, str):
                self.text += str
        def getOutput(self):
                return self.text
        def flush(self):
                os.sys.stdout.flush()#Needed?
                self.text = ''

def data_begin():
        """Signal the beginning of data to TeXmacs."""
        os.sys.stdout.write(chr(2))

def data_end():
        """Signal the end of data to TeXmacs."""
        os.sys.stdout.write(chr(5))
        os.sys.stdout.flush()

def texmacs_out(out_str):
        """Feed data back to TeXmacs.
        
        Output results back to TeXmacs, with the DATA_BEGIN,
        DATA_END control characters."""
        data_begin()
        os.sys.stdout.write(out_str)
        data_end()
        os.sys.stdout.flush()

def ps_out(ps_file):
        """Outputs Postscript within TeXmacs.

        According the the type of the argument the following
        scenarios can take place:       

        If the argument is a string and has more than one line, it
        will be processed as raw Postscript data.
        
        If the argument is a string, it's supposed to contain the
        filename of a Postscript file which will be  read ( if the
        file  has no extension, the defaults .ps and .eps will be
        tried.)
        
        If the argument is a file  or other object which provides a
        'read'  method, data will be obtained by calling such
        method.
        
        
        Implemented from suggestion by Alvaro Tejero Cantero.
        Implementation partially based on information provided
        by Mark Arrasmith.
        """
        
        if 'read' in dir(ps_file):
                data = ps_file.read()
                return chr(2)+'ps:'+data+chr(5)
                
        if ps_file.find('\n')>0:
                return chr(2)+'ps:'+ps_file+chr(5)
        
        ext_list = ['', '.eps', '.ps']
        if isinstance(ps_file, str):
                for ext in ext_list:
                        if os.path.exists(ps_file+ext):
                                ps_fd = file(ps_file+ext, 'r')
                                data = ps_fd.read()
                                ps_fd.close()
                                break
                else:
                        raise IOError('File \''+ps_file+'+'+str(ext_list)+'\' 
not found.')
        return chr(2)+'ps:'+data+chr(5)

def compose_output(data):
     """Do some parsing on the output according to its type."""
     if isinstance(data, str) and data == "":
                return 'verbatim: %s' % str(data)
     if isinstance(data, str) and data.find('Traceback') != -1:
                return 'verbatim: %s' % str(data)
     if isinstance(data, str) and data.find('Error:') != -1:
                return 'verbatim: %s' % str(data)
     if isinstance(data, int):
                return 'verbatim: %d' % data
     if isinstance(data, float):
                return 'verbatim: %f' % data
     if isinstance(data, unicode):
                data2=r''
                for c in data:
                        if c not in string.printable:
                                data2+='\\x%x' % ord(c)
                        else:
                                data2+=c
                data=data2
                return 'verbatim: %s' % str(data)

     data = latex(data,mode='inline')
     return 'latex: %s' % str(data)
   


def do_module_hierarchy(mod, attr):
        """Explore an object's hierarchy.
        
        Go through the objects hierarchy looking for
        attributes/methods to provide as autocompletion
        options.
        """
        dot = attr.find('.')
        if dot>0:
                if hasattr(mod, attr[:dot]):
                        next = getattr(mod, attr[:dot])
                        return do_module_hierarchy(next, attr[dot+1:])
        if isinstance(mod, dict):
                return dir(mod)
        else:
                return dir(mod)

def find_completion_candidates(cmpl_str, my_globals):
        """Harvest candidates to provide as autocompletion options."""
        
        haystack = 
my_globals.keys()+dir(my_globals['__builtins__'])+keyword.kwlist
        dot = cmpl_str.rfind('.')
        offset = None
        if dot>0:
                offset = len(cmpl_str[dot+1:])
                first_dot = cmpl_str[:dot].find('.')
                if first_dot<0:
                        mod_name = cmpl_str[:dot]
                        r_str = cmpl_str[dot+1:]
                else:
                        mod_name = cmpl_str[:first_dot]
                        r_str = cmpl_str[first_dot+1:]
                if mod_name in keyword.kwlist:
                        return None, []
                if os.sys.modules.has_key(mod_name):
                        haystack = 
do_module_hierarchy(os.sys.modules[mod_name], r_str)
                elif mod_name in my_globals.keys():
                        haystack = do_module_hierarchy(my_globals[mod_name], 
r_str)
                else:
                        haystack = do_module_hierarchy(type(mod_name), r_str)
                        
        return offset, filter(lambda x:x.find(cmpl_str[dot+1:])  ==  0, 
haystack)

def name_char(c):
        """Check whether a character is a valid symbol."""
        if c in '+-*/%<>&|^~ = !,:()[]{}':
                return ' '
        else:
                return c

def complete(cmd, my_globals):
        """Parse autocomplete command.
         
        Parse the command and return a suitable answer to
        give back to TeXmacs.
        """

        # Parse Texmacs command and extract string to
        # complete and offset to complete from.
        cmd = cmd.strip()[:-1]
        cmd_re = re.compile(r'"(.*)"\s+(\d+)')
        res = cmd_re.match(cmd)
        
        # if we don't match anything we return
        # no completion possibilities.
        if res is None:
                return 'scheme:(tuple "" "")'
                
        cmpl_str = res.group(1)
        pos_str = int(res.group(2))
        
        cmpl_str = cmpl_str[:pos_str]
        if len(cmpl_str)  ==  0:
                return 'scheme:(tuple "" "")'
        
        # We get the string after the last space character.
        # no completion is done for strings with spaces
        # within
        cmpl_str = str().join(map(name_char, cmpl_str))
        cmpl_str = cmpl_str.split()[-1]
        pos = len(cmpl_str)
        
        # no string after last space? return empty
        # completion
        if len(cmpl_str)  ==  0:
                return 'scheme:(tuple "" "")'
                
        # Find completion candidates and form a suitable
        # answer to Texmacs
        offset, cand = find_completion_candidates(cmpl_str, my_globals)
        if len(cand) == 0:
                res = '""'
        else:
                res = ''
        for c in cand:
                if offset is not None:
                        pos = offset
                res += '"%s" ' % c[pos:]
        return 'scheme:(tuple "'+cmpl_str+'" '+res+')'

##FIXME:  It should display the Python version, just as when one starts python.
texmacs_out("""verbatim:Python plugin for TeXmacs.
Please see the documentation in Help -> plugins -> Python
""")

my_globals = {}
# We insert into the session's namespace the 'ps_out' method.
my_globals['ps_out'] = ps_out

# As well as some documentation.
my_globals['__doc__'] = """Python plugin for TeXmacs.  It provides 
autocompletion, and can insert postscript files into TeXmacs.
        """

capt = Capture()
stdout_saved, os.sys.stdout  =  os.sys.stdout, capt
co = compile('import __builtin__ as __builtins__', 'tm_python', 'exec')
eval(co, my_globals)
os.sys.stdout = stdout_saved

# Main session loop.
while 1:
        line=raw_input();
        if line[0]  ==  DATA_COMMAND:
                if line[1:].find('(complete ')  ==  0:
                        #The replace is due to texmacs sending " as two 
characters
                        texmacs_out(complete(line[11:].replace("\\\"","\""), 
my_globals))
                continue
        lines=[line]
        while line<>"<EOF>":
                line=raw_input()
                if line == '': continue
                lines.append(line)
        text='\n'.join(lines[:-1])
        capt=Capture()
        try:
                out = eval(text, my_globals)
                result = out
        except:
                try:
                        stdout_saved, os.sys.stdout  =  os.sys.stdout, capt
                        co = compile(text, 'tm_python', 'exec')
                        eval(co, my_globals)
                        os.sys.stdout = stdout_saved
                        result = capt.getOutput()
                except Exception:
                        traceback.print_exc(file = os.sys.stdout, limit = 0)
                        os.sys.stdout = stdout_saved
                        result = capt.getOutput()
        del capt

        out = compose_output(result)
        texmacs_out(out.strip())
_______________________________________________
Texmacs-dev mailing list
Texmacs-dev@gnu.org
https://lists.gnu.org/mailman/listinfo/texmacs-dev

Reply via email to