running TCL through python by subprocess, but not giving any output

回眸只為那壹抹淺笑 提交于 2020-05-16 09:46:06

问题


I am trying to run my tcl script through python subprocess as follow:

import subprocess
>>> subprocess.Popen(["tclsh", "tcltest.tcl"])
<subprocess.Popen object at 0x0000000001DD4DD8>
>>> subprocess.Popen(["tclsh", "tcltest.tcl"], shell=True )
<subprocess.Popen object at 0x0000000002B34550>

I don't know if it is working or not, since I don't see any anything from it! my tcl script also has some packages from my company that causes errors when I use Tkinter, Tk, and eval,

import Tkinter
import socket

def TCLRun():
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 s.connect(('127.0.0.1', 5006))
 root = Tkinter.Tk()
## root.tk.eval('package require Azimuth-Sdk')
 tcl_script ="""
##package require Company-Sdk
## set players [ace_azplayer_list_players]
set players 2
puts $players 
##  if { $players != "" } {         
##  foreach player $players {   
##      set cmd ace_azplayer_remove_player
##      if { [catch { [ $cmd $player ] } err] } {   
##          puts " $cmd $player - $err" 
##          sleep 1 
##          }           
##      } 
##  } """
 # call the Tkinter tcl interpreter
 root.tk.call('eval', tcl_script)
 root.mainloop()

gives me this error

import TCLCall
>>> TCLCall.TCLRun()

Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    TCLCall.TCLRun()
  File "C:\Users\XXX\Desktop\PKT\TCLCall.py", line 24, in TCLRun
    root.tk.call('eval', tcl_script)
TclError: can not find channel named "stdout"

that's why I switched to subprocess. at least it doesn't give me error!

any idea how to run my tcl script with internal required package through python?!

Thanks


回答1:


To get the output from using subprocess.Popen, you can try the following:

import subprocess

p = subprocess.Popen(
    "tclsh tcltest.tcl",
    shell=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
print stdout
print stderr

It's entirely possible that the script you're running with subprocess.Popen is also generating an error, but isn't displaying since you aren't explicitly looking for it.

Edit:

To prevent some information from being lost in the comments below:

You probably have several potential errors here, or things you can try.

Either your tcl script by itself isn't able to import teapot properly, or some sort of interaction between the tcl script and the python script isn't working properly, or subprocess.Popen isn't correctly finding the teapot package from your path.

I would try debugging your programs in that order. First confirm that your tcl script works without using python or subprocess.Popen and just directly run it from the command line (for example, C:\Users\blah tclsh tcltest.tcl)

Then, after you've made sure your script work, bring in Python. From what I'm seeing, you don't have any problem with the python stuff, but with either your tcl script or some issue with your path.




回答2:


The whole point of subprocess.Popen is redirection of standard channels, so you can handle output programmatically instead of seeing it on your own standard output. Did you try handling it? How?

Maybe you don't need redirection at all: then os.system("tclsh tcltest.tcl") should be enough. Or maybe subprocess.Popen has other advantages for you -- then figure out how to disable redirection, or how to redirect child's stdout to your own stdout.




回答3:


I think I might have a solution for you. Or at least a different method to try. This example opens the TCL shell as a process. Then you pump commands to it as though you are on command line. Since you said your command line worked, I would think that this would too. This works for me on Windows with Python 3.7.6 and TCL 8.5.

There needs to be a little trickery. I've found solutions that require threads and all sorts of other overhead to get this job done but they just fell flat. What I came up with is simple and synchronous.

stdout.readline() will block. So if your TCL command doesn't throw anything back, you are dead in the water.

SO, you force something to come back by appending an innocuous TCL puts command that communicates back to the Python script that the job is done.

The other trick is that you need to "puts []" the command to force the output back into stdout.

If you need to source more TCL files, then add those in before you make your final call to what you are trying to run. Whatever you need to do on the cli, you do through this process.

My code has this all wrapped in a class with methods. I brought them all together here in-line for the example. Leave the errorInfo code in while debugging and remove as you see fit.

Note: You can also use TCL "info body " to read a script into a string variable and then run each line through the process. In essence, if in a Python debugging session, stepping through TCL. Sorry, this does not work too well. The workaround for comments does not work for opening curly braces.

Hope this helps someone out there.

EDIT: Using multi-line string handles comment lines.

import subprocess

print("------")
shell_path = r"<YOUR PATH TO THE TCL INTERPRETER SHELL>"
tcl_shell = subprocess.Popen(shell_path,
                    stdin =subprocess.PIPE,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    universal_newlines = True,
                    bufsize = 0)

#
# Use ONE of the "command"s below. I have them here in series for exmaples.
#

# Simple
command = r"set temp 5"

# Multiline with error --> This will give an error of course to see the error extraction in action
command = r"""
    set temp 5
    set temp2 [expr temp + 5]
"""

# Multiline working
command = r"""
    set temp 5
    set temp2 [expr $temp + 5]
"""

# More output
command = r"""
    puts "Starting process"
    set temp 5
    puts $temp
    set temp2 [expr $temp + 5]
"""

# Comments handled
command = r"# comment!"

# Be sure to leave the newline to handle comments
tcl_shell.stdin.write(f"""puts [{command}
                                ] \nputs \"tcl_shell_cmd_complete\"\n""")
# NOTE: tcl_shell.stdin.flush() does not seem to be needed. Consider if needed.
result = ""
line = tcl_shell.stdout.readline()
while line != "tcl_shell_cmd_complete\n":
    result += line
    line = tcl_shell.stdout.readline()

print(f"tcl_shell sent:\n{command}")
print(f"tcl_shell result:\n{result}".rstrip())

command_error_check = "puts $errorInfo"
tcl_shell.stdin.write(f"{command_error_check} \nputs \"tcl_shell_cmd_complete\"\n")
resultErr = ""
line = tcl_shell.stdout.readline()
while line != "tcl_shell_cmd_complete\n":
    resultErr += line
    line = tcl_shell.stdout.readline()

print(f"tcl_shell error info:\n{resultErr}")

tcl_shell.stdin.close()
tcl_shell.terminate()
tcl_shell.wait(timeout=0.5)
print("------")


来源:https://stackoverflow.com/questions/14264118/running-tcl-through-python-by-subprocess-but-not-giving-any-output

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!