Sublime Text 3 Python 3 print while running - python-3.x

I can't get Python 3 to print out anything while running a script in Sublime Text. I can get it to print out after the script has finished, but I need it to print as it goes.
Here's some example code
import time
for x in range(10):
print("Test")
time.sleep(1)
Using Python 3 as the build system, I get nothing for 10 seconds, and then 10 "Tests" printed all at once.
If I run the same script using the Python 2 build system, I get 1 "Test" printed out each second, which is what I want.
Similarly if I run the script using "python3 script.py" in the terminal I get 1 "Test" printed out each second.
Can anyone tell me how to make the build system in Sublime Text 3 print out Python 3 while it runs?

You have to open your Python 3 build system configuration file, and ensure that the -u option is supplied to the command line to be executed.
For example:
# ~/.config/sublime-text-3/Packages/User/Python3.sublime-build
{
"cmd": ["/usr/bin/python3.5", "-u", "$file"],
"selector": "source.python",
"file_regex": "file \"(...*?)\", line ([0-9]+)"
}
Quoting the documentation:
-u
Force the binary layer of the stdout and stderr streams (which is
available as their buffer attribute) to be unbuffered. The text I/O
layer will still be line-buffered if writing to the console, or
block-buffered if redirected to a non-interactive file.
With this option, the ouptut of your Python script will be correctly displayed into Sublime Text while running.

Related

Default message when Build System starts in Sublime Text

When you run Sublime Build and it finishes, there is a message [Finished ] (in the picture). I would like to add default message when every Build starts to be like "[Started]":
Also it would be nice to add local time when Build System started, e.g. [Started 22:20:23]
The are (at least) three ways to do this. The first way is to add the functionality to the beginning of your code, so it prints out the information you want. The disadvantage of this method is that the message is printed only when the code begins to run, not at the beginning of the build. Depending on the language you're using, whether it's compiled or interpreted, and the size of your codebase, this could be a significant lag.
The second method is to run your build through a shell file which executes using bash. On Windows, this requires that you have bash installed - Git Bash and Cygwin are two common ways of obtaining it. The following script accepts an arbitrary number of arguments, which it runs after printing "Started" and the date.
#!/bin/bash
echo "[Started at `date`]"
# check to see if we have at least 1 arg
if [ $1 ]
then
# replace this process w/ arg(s) so `exec.py` (the Sublime
# build system) gets the proper return value
exec "${#}"
fi
Save this file as build.sh somewhere in your PATH.
Now, take a look at the .sublime-build file for the build system you're using, specifically the "shell_cmd" or "cmd" line. If it's a "shell_cmd", all you'll need to do is copy and paste it (without the enclosing double quotes) into the build system below. If it's a "cmd", convert the array/list following "cmd": to a single string. So, for example, if you're using the default Python build system on Windows, "cmd": ["py", "-u", "$file"] would become py -u $file. Essentially, you're converting the array to what you would type at the command prompt, keeping Sublime-internal variables beginning with $ (like $file) intact.
Next, select Tools → Build System → New Build System…. Erase its contents and paste in the following template:
{
"shell_cmd": "bash -c \"build.sh new_cmd_goes_here\"",
"working_dir": "$file_path",
// "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
// "selector": "source.python",
// "env": {"PYTHONIOENCODING": "utf-8"}
}
replacing new_cmd_goes_here with the command string you just created in the step above. So, for our Python example, that line would become:
"shell_cmd": "bash -c \"build.sh py -u $file_name\"",
You can uncomment the commented-out lines in the build system template if you wish.
When you're done editing the build system, simply hit CtrlS to save, naming it something like Python (start message).sublime-build, for example. You don't need to change the directory the file is saved in, as Sublime will automatically put it in your Packages/User directory.
The third option is to modify Packages/Default/exec.py to fit your needs. This requires knowledge of Python and Sublime's internals. You can find the basics of how build systems work and how to extend them here.
Briefly, you would save Packages/Default/exec.py as Packages/User/exec_with_dt.py, setting the read_only flag to False if necessary. Next, change the name of the ExecCommand class to ExecWithDtCommand. Then, just after self.proc is defined as an AsyncProcess, add a line calling either self.append_string() (ST3) or self.write() (ST4) writing your desired string to the output. In ST4, I used:
from datetime import datetime as dt
self.write("[Started " + dt.now().strftime("%Y-%m-%d %H:%M:%S") + "]\n")
I haven't tested this in ST3, but the following should work there:
from datetime import datetime as dt
self.append_string(None, "[Started " + dt.now().strftime("%Y-%m-%d %H:%M:%S") + "]\n")
Save the file, then create a new build system with the following contents:
{
"target": "exec_with_dt",
"cmd": ["py", "-u", "$file"],
}
I don't recommend this approach unless you really know what you are doing, and the shell script method isn't sufficient for your needs. Other edits may need to be made to exec_with_dt.py to ensure complete parallel functionality with the original exec.py, so look through it carefully. For example, you may want to modify ExecEventListener to ExecWithDtEventListener and change its code to run the exec_with_dt command, just to keep everything in-house.

isatty() always returning False?

I want to pipe data via stdin to a Python script for onwards processing. The command is:
tail -f /home/pi/ALL.TXT | python3 ./logcheck.py
And the Python code is:
import sys
while 1:
if (sys.stdin.isatty()):
for line in sys.stdin:
print(line)
I want the code to continuously watch stdin and then process each row when received. The tail command is working when run on its own but the python script never outputs anything.
Checking isatty() it appears to always return False?
Help!
A TTY is when you use your regular terminal - as in opening up a python in your shell, and typing
BASH>python
>>>from sys import stdin
>>>stdin.isatty() #True
In your case the standard input is coming from something which is not a tty. Just add a not in the if statement.

Sublime 3 Path Variables

I am trying to set up a python 3 build path for sublime text 3 on a windows 10 pc. The system says that it cannot find the specified file, but I have already added the path and set the build system for python3. The error message I get when I try to build is below..
[WinError 2] The system cannot find the file specified
[cmd: ['python3', '-i', '-u', 'C:\\Users\\strinkjr\\Desktop\\Python Stuff\\errorSearch.py']]
[dir: C:\Users\strinkjr\Desktop\Python Stuff]
[path: C:\Users\strinkjr\Desktop\Python Stuff\]
[Finished]
My build environment file is as follows:
{
"cmd": ["python3", "-i", "-u", "$file"],
"file_regex": "^[ ]File \"C:/Users/strinkjr/AppData/Local/Programs/Python/Python36/python.exe\", line ([0-9]*)",
"selector": "source.python"
}
I am unsure if I set up the path incorrectly or if I set up the build environment incorrectly. (Maybe both)
There are a couple of overall problems you're having that are standing in your way here.
The first is that you didn't set the PATH correctly. The build output shows you the PATH as it's currently defined as far as the command execution is concerned:
[path: C:\Users\strinkjr\Desktop\Python Stuff\]
The PATH is the list of locations where windows will look for the program that you're trying to execute, so unless there is a python3.exe in this directory you've accidentally set the PATH to the location of the files that you're running and not the interpreter that's used to run them.
Secondly you're passing -i to the Python interpreter to get it to drop into interactive mode once it's done executing the script. Sublime doesn't let you interact with programs that you execute from within a sublime-build, so if you do this once your program finishes executing and goes into interactive mode, it's going to be effectively hung waiting for you to provide it input that you can't provide.
Your build system also contains this file_regex entry:
"file_regex": "^[ ]File \"C:/Users/strinkjr/AppData/Local/Programs/Python/Python36/python.exe\", line ([0-9]*)",
In a sublime-build file, the file_regex is used to be able to detect what lines in the program's output are errors so that Sublime can allow you to navigate between errors or flag them with inline errors if you have that option turned on.
Although this won't stop your programs from running, it will stop Sublime from being able to recognize errors because the name of the file is never going to match.
I would try a sublime-build file something like the following and see if that works better for you:
{
"shell_cmd": "python3 -u \"$file\"",
"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
"selector": "source.python",
"env": {
"PYTHONIOENCODING": "utf-8",
"PATH": "$PATH;C:/Users/strinkjr/AppData/Local/Programs/Python/Python36/"
},
}
This removes the -i argument to stop the interpreter from going interactive in order to stop any problems and uses shell_cmd instead of cmd to provide the command, which changes the format slightly.
The file_regex here is one that will match regular python errors, which is similar to the one you already provided but without the reference to the Python executable.
The big addition here is a couple of environment variables. The first one ensures that Python knows that it should use utf-8 to generate output, since that's what the Sublime console is expecting it to use. That stops you from getting potential errors if you try to display non-ascii output.
This also applies a new PATH that includes the existing path and also adds to it the path that looks like it might be where your Python is installed based on the files you were already using.
That part may need adjustment if the location is not correct; alternatively you can remove the PATH portion of the sublime-build and modify your PATH environment variable as appropriate instead. Note that you may need to restart Sublime if you do that in order for it to see the change.

How to execute an external execution file with parameters in Python 3?

I want to execute an exe file using Python 3.4.
That is,
C:/crf_test.exe -m input.txt output.txt
When I executed this at the command line, the result was:
Go SEARCH
to O
...
But, when I executed this in Python like this:
import os
os.startfile('crf_test.exe -m model.txt test.txt')
Nothing happened (I mean appeared in the result window.)
Using os.popen() you can execute and read commands:
cmd = os.popen(r'crf_test.exe -m model.txt test.txt')
result = cmd.read()

python subprocess.readline() blocking when calling another python script

I've been playing with using the subprocess module to run python scripts as sub-processes and have come accross a problem with reading output line by line.
The documentation I have read indicates that you should be able to use subprocess and call readline() on stdout, and this does indeed work if the script I am calling is a bash script. However when I run a python script readline() blocks until the whole script has completed.
I have written a couple of test scripts that repeat the problem. In the test scripts I attmept to run a python script (tst1.py) as a sub-process from within a python script (tst.py) and then read the output of tst1.py line by line.
tst.py starts tst1.py and tries to read the output line by line:
#!/usr/bin/env python
import sys, subprocess, multiprocessing, time
cmdStr = 'python ./tst1.py'
print(cmdStr)
cmdList = cmdStr.split()
subProc = subprocess.Popen(cmdList, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
while(1):
# this call blocks until tst1.py has completed, then reads all the output
# it then reads empty lines (seemingly for ever)
ln = subProc.stdout.readline()
if ln:
print(ln)
tst1.py simply loops printing out a message:
#!/usr/bin/env python
import time
if __name__ == "__main__":
x = 0
while(x<20):
print("%d: sleeping ..." % x)
# flushing stdout here fixes the problem
#sys.stdout.flush()
time.sleep(1)
x += 1
If tst1.py is written as a shell script tst1.sh:
#!/bin/bash
x=0
while [ $x -lt 20 ]
do
echo $x: sleeping ...
sleep 1
let x++
done
readline() works as expected.
After some playing about I discovered the situation can be resolved by flushing stdout in tst1.py, but I do not understand why this is required. I was wondering if anyone had an explanation for this behaviour ?
I am running redhat 4 linux:
Linux lb-cbga-05 2.6.9-89.ELsmp #1 SMP Mon Apr 20 10:33:05 EDT 2009 x86_64 x86_64 x86_64 GNU/Linux
Because if the output is buffered somewhere the parent process won't see it until the child process exists at that point the output is flushed and all fd's are closed. As for why it works with bash without explicitly flushing the output, because when you type echo in a most shells it actually forks a process that executes echo (which prints something) and exists so the output is flushed too.

Resources