Process vi yank buffer with python - python

I would like to add a key map to VI. The idea is to yank text and pass it to a python code.
Subsequently this python code can be used to preform some manipulations on the yanked text contained in the buffer #"
To do so, I added the following line to .vimrc
:map <F2> :echo system("python /tmp/t.py ".shellescape(#")) <Enter>
By pressing F2 the python script would run the code with #" as input. The problem is, that the yanked buffer contains the end line \ is a column is yanked with <C-r>V.
The buffer looks like this for a column input
1.233\
1.111\
1.222
I would like to accomplish the following
1) Don't include \ in the yank buffer is possible
2) make the pyhon script globally visible from within VI. Right now, it has to be in the same folder as the VI file, or an absolute path is needed
The latter one can be properly done by placing the script in the python site packages and importing it, but I guess, that more knowledgeble people might have better ideas. I remember seeing this done, by putting the python script in a special Vi folder.
the python code:
import sys
def to_float(v):
if len(v)>0 and v != "":
try:
return float(v)
except ValueError:
#print('yanked wrong text 1 "%s" '%v)
try:
return float(v[:-1])
except ValueError:
#print('yanked wrong text 2 "%s" '%v)
return 0.
else :
return 0.
def func(v):
s=0.
for i in v[1].split():
#print('from py >> %s <<'%i)
s=s+to_float(i)
print(">>>Summ: %17.8E"%s)
func(sys.argv)

Python-inside-Vim (as long as it is properly set up). Just dump this into your .vimrc (and make sure you don't add any indent to anything between python and EOF):
python << EOF
import vim
def float_or_0(value):
try:
return float(value)
except:
return 0.0
def sum_column():
lines = vim.eval('#"').splitlines()
total = sum(float_or_0(x) for x in lines)
print(">>>Summ: %17.8E" % total)
EOF
xmap <F2> y:py sum_column()<Enter>
Select your column, and hit F2. Magic. No hassle with transfering text through shell command line.
Or, you could select one of the strategies from Quickly calculate the total of a column of numbers.

Related

Use Vim Retab to solve TabError: inconsistent use of tabs and spaces in indentation?

Apologize for the newbie question, but I have read the manual, this question, and tried several times without results I expected.
So I was using vim to edit a file (attached). But when running, I got the TabError: inconsistent use of tabs and spaces in indentation error.
Here is what I have tried:
Open the file with Vim. type :retab, and :x. Run the file again. Still got the TabError message.
Open the file again and type :retab! and :x. Run the file again. Still got the TabError message.
Open the file again and type :retab! 4 and :x. Run the file again. This time it works but I have no idea why? Plus, in the files indentation seems excessively long. (I read here that the editor might display 8 spaces for a tab)
My questions are:
What does :retab, :retab!, and :retab! 4 mean?
Why doesn't :retab work on my file?
#!/usr/bin/env python
#Reduce function for computing matrix multiply A*B
#Input arguments:
#variable n should be set to the inner dimension of the matrix product (i.e., the number of columns of A/rows of B)
import sys
import string
import numpy
#number of columns of A/rows of B
n = int(sys.argv[1])
#Create data structures to hold the current row/column values (if needed; your code goes here)
currentkey = None
alist = [] # list for elelents in A
blist = [] # list for elements in B
# input comes from STDIN (stream data that goes to the program)
for line in sys.stdin:
#Remove leading and trailing whitespace
line = line.strip()
#Get key/value
key, value = line.split('\t',1)
print(key, value)
#Parse key/value input (your code goes here)
key = (key.split(',', 1)[0], key.split(',',1)[1])
value = (value.split(',', 1)[0], value.split(',',1)[1], value.split(',',1)[2])
#If we are still on the same key...
if key==currentkey:
#Process key/value pair (your code goes here)
# store all values in a lisl
if value[0]=='A':
alist.append([value[1], value[2]])
else:
blist.append([value[1], value[2]])
#Otherwise, if this is a new key...
else:
#If this is a new key and not the first key we've seen, i.e. currentkey!=None
if currentkey:
#compute/output result to STDOUT (your code goes here)
alist = sorted(alist)
blist = sorted(blist)
newlist = [a[1]*b[1] for a,b in zip(alist, blist)]
res = newlist.sum()
print(currentkey, res)
currentkey = key
#Process input for new key (your code goes here)
Just type :help retab in Vim and read. I think I can't explain it better than the help. Maybe you are missing the optional range part; use % prefix to apply to the whole file. Also useful is :set list to show you every chars; this will show you the tabs and line ends (disable with :set nolist) and :set <name> with no value to see the current value, ex : set tabstop or followed by some value to set.
By showing all chars, enabling and disabling expansion of tabs into spaces with :set expandtab and :set noexpandtab, setting tabstop and by using for ex. :retab! 4 you can play your way around and switch from tabs only and spaces only, and change the tab column width.
This link, vim settings for python might be useful also
Esc
:%s/\t/ /g
It means to replace the tab with 4 spaces. Hit Enter
Esc
:wq
While just trying to run :retab might not work if you have copy pasted code and started to edit it.
You can try setting the vim/vi settings first:
Check if you have ~/.vim/vimrc file else run sudo mkdir ~/.vim && sudo touch ~/.vim/vimrc to create settings file
once created edit vimrc to add below snippet
set softtabstop=4
set tabstop=4
set shiftwidth=4
set expandtab
open your file using vim/vi and run :retab

how to import files from command line in python

I am using python and I am supposed to read a file from command line for further processing. My input file has a binary that should be read for further processing. Here is my input file sub.py:
CODE = " \x55\x48\x8b\x05\xb8\x13\x00\x00"
and my main file which should read this is like the following:
import pyvex
import archinfo
import fileinput
import sys
filename = sys.argv[-1]
f = open(sys.argv[-1],"r")
CODE = f.read()
f.close()
print CODE
#CODE = b"\x55\x48\x8b\x05\xb8\x13\x00\x00"
# translate an AMD64 basic block (of nops) at 0x400400 into VEX
irsb = pyvex.IRSB(CODE, 0x1000, archinfo.ArchAMD64())
# pretty-print the basic block
irsb.pp()
# this is the IR Expression of the jump target of the unconditional exit at the end of the basic block
print irsb.next
# this is the type of the unconditional exit (i.e., a call, ret, syscall, etc)
print irsb.jumpkind
# you can also pretty-print it
irsb.next.pp()
# iterate through each statement and print all the statements
for stmt in irsb.statements:
stmt.pp()
# pretty-print the IR expression representing the data, and the *type* of that IR expression written by every store statement
import pyvex
for stmt in irsb.statements:
if isinstance(stmt, pyvex.IRStmt.Store):
print "Data:",
stmt.data.pp()
print ""
print "Type:",
print stmt.data.result_type
print ""
# pretty-print the condition and jump target of every conditional exit from the basic block
for stmt in irsb.statements:
if isinstance(stmt, pyvex.IRStmt.Exit):
print "Condition:",
stmt.guard.pp()
print ""
print "Target:",
stmt.dst.pp()
print ""
# these are the types of every temp in the IRSB
print irsb.tyenv.types
# here is one way to get the type of temp 0
print irsb.tyenv.types[0]
The problem is that when I run "python maincode.py sub.py' it reads the code as content of the file but its output is completely different from when I directly add CODE into the statement irsb = pyvex.IRSB(CODE, 0x1000, archinfo.ArchAMD64()). Does anyone know what is the problem and how can I solve it? I even use importing from inputfile but it does not read a text.
Have you considered the __import__ way?
You could do
mod = __import__(sys.argv[-1])
print mod.CODE
and just pass the filename without the .py extension as your command line argument:
python maincode.py sub
EDIT: Apparently using __import__ is discouraged. Instead though you can use importlib module:
import sys,importlib
mod = importlib.import_module(sys.argv[-1])
print mod.CODE
..and it should work the same as using __import__
If you need to pass a path to the module, one way is if in each of the directories you added an empty file named
__init__.py
That will allow python to interpret the directories as module namespaces, and you can then pass the path in its module form: python maincode.py path.to.subfolder.sub
If for some reason you cannot or don't want to add the directories as namespaces, and don't want to add the init.py files everywhere, you could also use imp.find_module. Your maincode.py would instead look like this:
import sys, imp
mod = imp.find_module("sub","/path/to/subfolder/")
print mod.code
You'll have to write code which breaks apart your command line input into the module part "sub" and the folder path "/path/to/subfolder/" though. Does that make sense? Once its ready you'll call it like you expect, python maincode.py /path/to/subfolder/sub/
you're reading the code as text, while when reading as file you're likely reading as binary
you probably need to convert binary to text of vice-versa to make this work
Binary to String/Text in Python

Efficient way to find a string based on a list

I'm new to scripting and have been reading up on Python for about 6 weeks. The below is meant to read a log file and send an alert if one of the keywords defined in srchstring is found. It works as expected and doesn't alert on strings previously found, as expected. However the file its processing is actively being written to by an application and the script is too slow on files around 500mb. under 200mb it works fine ie within 20secs.
Could someone suggest a more efficient way to search for a string within a file based on a pre-defined list?
import os
srchstring = ["Shutdown", "Disconnecting", "Stopping Event Thread"]
if os.path.isfile(r"\\server\\share\\logfile.txt"):
with open(r"\\server\\share\\logfile.txt","r") as F:
for line in F:
for st in srchstring:
if st in line:
print line,
#do some slicing of string to get dd/mm/yy hh:mm:ss:ms
# then create a marker file called file_dd/mm/yy hh:mm:ss:ms
if os.path.isfile("file_dd/mm/yy hh:mm:ss:ms"): # check if a file already exists named file_dd/mm/yy hh:mm:ss:ms
print "string previously found- ignoring, continuing search" # marker file exists
else:
open("file_dd/mm/yy hh:mm:ss:ms", 'a') # create file_dd/mm/yy hh:mm:ss:ms
print "error string found--creating marker file sending email alert" # no marker file, create it then send email
else:
print "file not exist"
Refactoring the search expression to a precompiled regular expression avoids the (explicit) innermost loop.
import os, re
regex = re.compile(r'Shutdown|Disconnecting|Stopping Event Thread')
if os.path.isfile(r"\\server\\share\\logfile.txt"):
#Indentation fixed as per comment
with open(r"\\server\\share\\logfile.txt","r") as F:
for line in F:
if regex.search(line):
# ...
I assume here that you use Linux. If you don't, install MinGW on Windows and the solution below will become suitable too.
Just leave the hard part to the most efficient tools available. Filter your data before you go to the python script. Use grep command to get the lines containing "Shutdown", "Disconnecting" or "Stopping Event Thread"
grep 'Shutdown\|Disconnecting\|"Stopping Event Thread"' /server/share/logfile.txt
and redirect the lines to your script
grep 'Shutdown\|Disconnecting\|"Stopping Event Thread"' /server/share/logfile.txt | python log.py
Edit: Windows solution. You can create a .bat file to make it executable.
findstr /c:"Shutdown" /c:"Disconnecting" /c:"Stopping Event Thread" \server\share\logfile.txt | python log.py
In 'log.py', read from stdin. It's file-like object, so no difficulties here:
import sys
for line in sys.stdin:
print line,
# do some slicing of string to get dd/mm/yy hh:mm:ss:ms
# then create a marker file called file_dd/mm/yy hh:mm:ss:ms
# and so on
This solution will reduce the amount of work your script has to do. As Python isn't a fast language, it may speed up the task. I suspect it can be rewritten purely in bash and it will be even faster (20+ years of optimization of a C program is not the thing you compete with easily), but I don't know bash enough.

Terminal screen coordinates in python

I'm newbie in using curses lib. I want to make a python program (running in a DOS terminal) that can return the cursor to the start of the current line allowing subsequent output to overwrite what was previously written there. I tried to call a shellscript from python to do it, but I beleive it may exist a better way for doing it.
If all you want to do is rewrite the current line, just print a return char "\r". For example, this prints "ABCdef":
# print 'abcdef' then backup to start of line, then print 'ABC'
# (then print the normal cr/lf that 'print' always does)
print "abcdef\rABC"
(Also helpful to know that you can suppress the cr/lf by ending your print with a trailing comma...)
.

Notepad++ Automatic Detection of Python Tabs or Spaces

I normally write code with tabs but many python libraries use spaces. Is there any way for Notepad++ to automatically detect how the file is formatted and have it automatically switch to using spaces when the file is already formatted that way?
BTW, I know there was already an SO question on how to change Notepad++'s tab format. But it would be better if it automatically changed based on the current file's formatting.
If you install the "Python Script" plugin for Notepad++, you can write code to automatically switch between tabs and spaces.
Here's how:
In the menu: Plugins -> Python Script -> Configuration, and set Initialization to ATSTARTUP. When Notepad++ starts, the startup.py script will run.
Find startup.py and edit it. On my PC its path is c:\Program Files\Notepad++\plugins\PythonScript\scripts\startup.py, add the following code to startup.py.
The function buffer_active() is called every time when you switch tab, and guess_tab() checks whether the text is using tab indent or not. You can show the Python console to debug the code.
def guess_tab(text):
count = 0
for line in text.split("\n"):
indents = line[:len(line)-len(line.lstrip())]
if "\t" in indents:
count += 1
if count > 5:
return True
else:
return False
def buffer_active(arg):
editor.setBackSpaceUnIndents(True)
use_tab = guess_tab(editor.getText())
editor.setUseTabs(use_tab)
sys.stderr.write( "setUseTabs %s\n" % use_tab )
notepad.clearCallbacks([NOTIFICATION.BUFFERACTIVATED])
notepad.callback(buffer_active, [NOTIFICATION.BUFFERACTIVATED])
This is only an example, feel free to make guess_tab() better yourself, maybe use a global dict to cache the result and speedup the callback function.
Here is an improved version based on HYRY's answer :
Works on the startup tab (when you launch notepad++ to open a file)
Doesn't need a minimal amount of rows to trigger indentation detection. Indentation guess is based on the first encountered indented line.
Keeps indentation defaults when indentation cannot be detected
Very efficient, doesn't slow down Notepad++ when opening big files (tested on a 220 MB file, indentation detection takes only < 300 ms)
Available for download here : https://gist.github.com/vincepare/8a204172d959defb2122
import re
import time
def indent_guess_tab(text):
for line in text.split("\n"):
pattern = re.compile("^( {4,}|\t)")
match = pattern.match(line)
if (match):
return True if ("\t" in match.group(1)) else False
def indent_auto_detect(arg):
start = time.clock()
# Get text sample
maxLen = 500000
len = editor.getTextLength()
len = len if len < maxLen else maxLen
sample = editor.getTextRange(0, len)
# Indent set
current_use_tab = editor.getUseTabs()
use_tab = indent_guess_tab(sample)
if (use_tab != None and use_tab != current_use_tab):
console.write("Indent use tab switch (%s => %s)\n" % (current_use_tab, use_tab))
editor.setUseTabs(use_tab)
end = time.clock()
console.write("Indentation detection took %s ms\n" % (round((end-start)*1000, 3)))
notepad.clearCallbacks([NOTIFICATION.BUFFERACTIVATED, NOTIFICATION.READY])
notepad.callback(indent_auto_detect, [NOTIFICATION.BUFFERACTIVATED])
notepad.callback(indent_auto_detect, [NOTIFICATION.READY])
console.write("Automatic indentation detection started\n")
indent_auto_detect(None)
Nope!
You can always just change them (to tabs, of course) to suit your needs with Replace All ( , \t) in extended mode.

Categories

Resources