Running TCL code (on an existing TCL shell) from P

2019-09-17 20:44发布

Introduction

I recently started working on a legacy product, under Linux, which incorporates a builtin TCL shell. Due to company limitations, I can't gain control to "behind the scenes" and all of the code I can write must be run under this TCL shell, handling the pre-defined clumsy TCL API of the product.

I found myself wondering a few times whether it will be possible to patch some Python into this setup, as some solutions seem more legitimate in Python than in TCL. By patching Python I mean: either calling the Python code from the TCL code itself (which can be done with Elmer, for example), or to use Python from outside of the product to wrap the TCL API (for which I found no "classic" solution).

The Problem

Given that the product already has an existing TCL shell in it, most solutions I browsed through (e.g. Tkinter) can't be used to run the TCL code from Python. I need to "inject" the code to the existing shell.

Solutions Considered

As a solution, I thought about bringing up a simple server on the TCL side which runs simple commands it receives from the Python side. I wrote a small demo and it worked. This enabled me to write a nice, class based, wrapper in Python for the clumsy TCL API, also manage a command queue, etc.

Another two solutions I thought of is forking the software from Python and playing with the read/write file descriptors or connecting through FIFO rather than a socket.

However, I wondered whether I'm actually doing it right or can you suggest a better solution?

Thanks in advance.

1条回答
Explosion°爆炸
2楼-- · 2019-09-17 21:37

First: If you just want a class based OO system to write your code in, you don't need python, Tcl can do OO just fine. (built in with 8.6, but there are quite a few options to get OO features, classes etc. in older versions too, e.g. Tcllibs SNIT or STOOOP

If you still feel Python is the superior tool for the task at hand (e.g. due to better library support for some tasks), you can 'remote control' the Tcl interpreter using the Tcllib comm package. This needs a working event loop in the Tcl shell you want to control, but otherwise it is pretty simple to do.

In your Tcl shell, install the Tcllib comm package. (ask again if you need help with that)

Once you have that, start the comm server in your Tcl shell.

package require comm
set id [::comm::comm self]
# write ID to a file
set fd [open idfile.txt w]
puts $fd $id
close $fd
proc stop_server {} {set ::forever 1 }
# enter the event loop
vwait forever

On the python side, you do nearly the same, just in Tkinter code.

Basically like this:

import Tkinter
interp = Tkinter.Tcl()
interp.eval('package require comm')

# load the id
with open('idfile.txt') as fd:
    comm_id = fd.read().strip()

result = interp.eval(
    'comm::comm send {0!s} {1!s}'.format(comm_id, '{puts "Hello World"}')

Using that python code and your shell, you should see Hello World printed in your shell.

Read the comm manual for more details, how to secure things, get callbacks etc. comm

If you don't like the tkinter burden, you can implement the wire procotol for comm too, should not be too hard in Twisted or with the new async support in Python 3.x. The on the wire protocol is documented in: comm wire protocol

查看更多
登录 后发表回答