|
#!/usr/bin/python3 |
|
# -*- coding: utf-8 -*- |
|
##========================================================= |
|
## Dec 1, 2016 - 17 Jan 2017 |
|
## python-padding.py |
|
## |
|
## a Python script |
|
## to add spacing |
|
## to Python scripts. |
|
## |
|
## Rather recursive in that regard. |
|
## Mildly meta; hardly haiku. |
|
## |
|
## Eli Innis |
|
## Twitter : @Doyousketch2 |
|
## Email : Doyousketch2 @ yahoo.com |
|
## |
|
## GNU GPLv3 www.gnu.org/licenses/gpl-3.0.html |
|
##========================================================= |
|
## libs ------------------------------------------------- |
|
|
|
import os ## used to check if file exists |
|
import sys ## used to get commandline args |
|
import easygui as eg ## Graphical User Interface |
|
import fileinput ## line-by-line file edit |
|
|
|
## Note: ------------------------------------------------ |
|
## If you don't have easygui, try one of these methods: |
|
## sudo pip3 install easygui |
|
## sudo python3 -m pip install easygui |
|
##========================================================= |
|
## user set vars ---------------------------------------- |
|
|
|
tabs = 4 ## you can set the number of spaces you'd like your tabs to be: |
|
|
|
divlength = 60 ## width of dividers. half of rightCol looks nice, center of page. |
|
## comments will scoot over to rightCol, |
|
rightCol = 120 ## how far would you like them to go? |
|
|
|
## 120 shows up on 1024 x 768 display. (default 10 point Monospace font) |
|
## 136 1152 x 864 |
|
## 152 1280 x 1024 |
|
## 173 1440 x 900 |
|
|
|
leftCol = 10 ## all comments before this column won't be moved to the right, |
|
## because they may have been intentionally placed where they are. |
|
|
|
## we don't need to put extra spaces if there's already a space, |
|
## or if a decimal point is in a number. |
|
|
|
bypass = [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] |
|
|
|
symbols = [':', '<', '>', '+', '-', '*', '/', '%'] |
|
|
|
symbolpass = ['', ' ', '#', '[', ']', ':', '!', '<', '>', '=', '+', '-', '*', '/', '%'] |
|
|
|
eqpass = ['<', '!', '=', '>'] ## '+' and '-' if you desire |
|
|
|
|
|
##========================================================= |
|
## blank containers, don't change ----------------------- |
|
|
|
continyou = True ## "continue" is a Python keyword... |
|
## so I'm using an intentional misspelling as a variable name. |
|
ext = '' |
|
div1 = '' |
|
div2 = '' |
|
path = '' |
|
space = '' |
|
files = [''] |
|
tab = ' ' ## don't change tab character. |
|
|
|
howmany = 'One' ## defaults to One, in case a commandline arg is passed. |
|
## otherwise, dialog box will ask how many. |
|
##========================================================= |
|
## functs ----------------------------------------------- |
|
|
|
|
|
for x in range(0, tabs) : ## calculate tab spacing from the value given. |
|
space += ' ' |
|
|
|
for x in range(0, divlength) : ## create long runs of these chars. |
|
div1 += '=' |
|
div2 += '-' |
|
## we need to know what file we are actually scanning, so we'll check for commanline arg |
|
## that may have been passed along, when originally calling this python script. |
|
try : |
|
filename = str(sys .argv[1]) ## sys.argv returns the command, as well as the args. |
|
## so we skip element 0, and convert what comes after the space into a string. |
|
|
|
if os .path .isfile(filename) == 0 : ## check if the file we specified actually exists |
|
raise ## if not, raise an exception, and jump down to file open box. |
|
|
|
except : ## if no args were passed, then we open a file dialog box. |
|
|
|
## buttonbox(msg='', title=' ', choices=('Button[1]', 'Button[2]', 'Button[3]'), |
|
## image=None, images=None, default_choice=None, cancel_choice=None, callback=None, run=True) |
|
howmany = eg .buttonbox('How howmany files do you wish to pad?', 'python-padding.py', ('One', 'Few', 'Many')) |
|
|
|
## fileopenbox(msg=None, title=None, default='*', filetypes=None, multiple=False) |
|
filename = eg .fileopenbox('Open a file to pad', 'PythonPadding', '*.py', ['*.py', "Python files"]) |
|
|
|
if type(filename) != str : ## if we close the dialog box, the filetype is blank, and isn't a string, |
|
sys .exit() ## go ahead and close program at this point, because we have no file. |
|
|
|
|
|
##========================================================= |
|
## pad script ------------------------------------------- |
|
|
|
|
|
def pad(this) : |
|
linenum = 0 |
|
imports = 0 |
|
hashbang = 0 |
|
|
|
for fullline in fileinput .input([this], inplace = True) : ## iterate through file contents, line-by-line. |
|
line = (fullline .rstrip() + '\n') ## strip trailing spaces |
|
if linenum == 0 : ## find out if it has a python header |
|
if '#!/' in line: |
|
hashbang = 1 |
|
if linenum == 1 or linenum == 2 : ## if we're on the first couple lines, |
|
if hashbang > 0 : |
|
if '# -*- coding:' not in line: ## and it's not coding info |
|
if (line[0] != '#' and line[0] != '\n' and line[0] != ''): ## are we past the header? |
|
sys .stdout .write('##' + div1 + '\n') ## write ===== |
|
sys .stdout .write('##' + div2 + '\n\n') ## write ----- |
|
hashbang = 0 |
|
|
|
if 'import ' in line and 'if' not in line : ## write space after imports |
|
imports = 1 |
|
else : |
|
if imports == 1 : |
|
if (line[0] != '#' and line[0] != '\n' and line[0] != ''): ## if we're past the imports |
|
sys .stdout .write('\n##' + div1 + '\n') ## write ===== |
|
sys .stdout .write('##' + div2 + '\n\n') ## write ----- |
|
imports = 0 |
|
|
|
## done with header, on to hashes ----------------------- |
|
|
|
pos = 0 |
|
hashes = 0 |
|
while (pos < len(line) - 1) : ## scroll through each character in the line |
|
char = line[pos] |
|
prevpos = (pos - 1) |
|
nextpos = (pos + 1) |
|
nextchar = line[nextpos] |
|
|
|
if pos == 0 : ## there is no previous char for the 0'th position, |
|
before = '' ## default to a blank char. |
|
prevchar = '' ## ditto |
|
else : |
|
before = line[: pos] ## get everything before char |
|
prevchar = line[prevpos] ## ditto, but for 1 char |
|
|
|
if (before .count('\'') % 2 == 0 and before .count('\"') % 2 == 0): ## don't mod quoted material. |
|
|
|
if char == '#': ## hashes |
|
hashes += 1 |
|
if hashes > 2 : ## if we have a string of ##### greater than 2 together, |
|
if before[0] is '#': |
|
after = line[nextpos :] ## get everything after char |
|
line = (before + '=' + after) ## Replace long strings of ##### with ===== |
|
|
|
elif pos > leftCol and nextchar == ' ' : ## if we have 1 or 2 hashes + space, it's a comment |
|
if len(line) < rightCol and 'import ' not in line : |
|
after = line[nextpos :] ## get everything after char. |
|
if hashes == 2 : |
|
before = line[: prevpos] ## chop off the first one |
|
char = '##' ## merge it, so it isn't left hanging |
|
gap = rightCol - len(before) - len(char) - len(after) + 1 ## how far to margin? |
|
if gap > 0 : ## if there's a bit of space left, |
|
padding = ' ' * gap ## calculate how much space we can insert, |
|
line = (before + padding + char + after) ## scoot comments over to the edge. |
|
pos = rightCol |
|
|
|
## done with hashes, scan for other chars --------------- |
|
|
|
else : |
|
hashes = 0 ## if it's not a # symbol, reset counter. |
|
if '#' not in before: ## don't bother with comments. |
|
|
|
if char is tab : |
|
after = line[nextpos :] ## get everything after char |
|
line = (before + space + after) ## concatenate results, spaces instead of tab |
|
|
|
elif char is '=' : ## equals sign |
|
if prevchar not in symbolpass : ## pad before |
|
char = ' =' |
|
if prevchar not in eqpass and nextchar not in eqpass and before[-2] is not ' ' : |
|
char = (' ' + char) |
|
if nextchar not in symbolpass : ## pad after |
|
char += ' ' |
|
if prevchar not in eqpass and nextchar not in eqpass and line[pos + 2] is not ' ' : |
|
char += ' ' |
|
after = line[nextpos :] |
|
line = (before + char + after) |
|
|
|
elif char is '.' : ## if we find a dot, |
|
if prevchar not in bypass : ## not a number, or already has a space before it. |
|
after = line[nextpos :] ## get everything after char |
|
line = (before + ' ' + char + after) ## join results, with space before . |
|
|
|
elif char is ',' : ## if we find a comma, |
|
if (line[nextpos] != ' ') : ## if there is no space after comma already |
|
after = line[nextpos :] ## get everything after char |
|
line = (before + char + ' ' + after) ## join results + space after comma |
|
|
|
elif char in symbols : ## try our list of symbols |
|
padbefore = 0 |
|
padafter = 0 |
|
if prevchar not in symbolpass : ## only pad if it's not in the list |
|
padbefore = 1 |
|
if nextchar not in symbolpass : |
|
if char is not '-' : ## don't pad negative numbers |
|
padafter = 1 |
|
elif nextchar not in bypass : |
|
padafter = 1 |
|
|
|
if (padbefore == 1 or padafter == 1) : |
|
after = line[nextpos :] ## get everything after char |
|
|
|
if (padbefore == 1 and padafter == 1) : |
|
line = (before + ' ' + char + ' ' + after) ## join, spaces surrounding char |
|
elif padbefore == 1 : |
|
line = (before + ' ' + char + after) ## join results, space before char |
|
elif padafter == 1 : |
|
line = (before + char + ' ' + after) ## join results, space after char |
|
|
|
## next char -------------------------------------------- |
|
|
|
pos += 1 |
|
|
|
## passed rightCol? try to trim line ------------------- |
|
|
|
if len(line) > rightCol : |
|
if '#' in line : ## don't trim instruction sets, only comments |
|
loc = (len(line) - 1) ## find current location at end of line |
|
gap = (loc - rightCol) ## how far it sticks over edge |
|
while gap > 0 and loc > 0 : ## only continue if we have space to work with |
|
if line[loc] is '#' : ## hunt for hashes |
|
if '#' not in line[: loc] : ## first hash? |
|
prevloc = (loc - 1) |
|
if line[prevloc] is ' ' : ## empty space before it? |
|
line = (line[: prevloc] + line[loc :]) ## snip out the space |
|
gap -= 1 ## mind the gap |
|
loc -= 1 ## head back 'till gap cleared, or at beginning of line |
|
|
|
## done with line, write it ----------------------------- |
|
|
|
out = (line .rstrip() + '\n') ## padding symbols may have added trailing spaces, if so, strip them |
|
sys .stdout .write(out) ## write modified line back to file |
|
linenum += 1 ## on to the next one |
|
|
|
|
|
##========================================================= |
|
## other scripts ---------------------------------------- |
|
|
|
|
|
def getpath(item) : ## find path for current item |
|
global path ## access container |
|
splitem = item .split('/') ## chop the filename at every / symbol |
|
everythinguptoname = splitem[0 : -1] ## grab everything up to the last / symbol |
|
path = '/' .join(everythinguptoname) ## piece all those separate dir names together again |
|
path += '/' ## add trailing slash |
|
return path |
|
|
|
|
|
def getext(item) : ## find extension for current item |
|
global ext ## access container |
|
splitem = item .split('.') ## chop the filename at every / symbol |
|
ext = splitem[-1] ## return last entry in splitem list |
|
return ext |
|
|
|
##========================================================= |
|
## main ------------------------------------------------- |
|
|
|
if howmany == 'One' : |
|
pad(filename) |
|
## msgbox(msg='(message)', title=' ', ok_button='OK', image=None, root=None) |
|
eg .msgbox('File padded. Thank you.\n\n' + str(filename), 'python-padding.py') |
|
sys .exit() |
|
|
|
elif howmany == 'Few' : |
|
while continyou == True : ## keep opening dialog box while true. |
|
## variable never gets a chance to change tho, it'll sys.exit instead. |
|
getpath(filename) |
|
pad(filename) ## pad where necessary |
|
files .append(filename) ## add name to list, for reference |
|
|
|
## fileopenbox(msg=None, title=None, default='*', filetypes=None, multiple=False) |
|
filename = eg .fileopenbox('Open a file to pad', 'PythonPadding', path + '/*.py', ['*.py', 'Python files']) |
|
|
|
if type(filename) != str : ## if we close the dialog box, the filetype is blank, and isn't a string, |
|
names = '\n' .join(files) ## concatenate list, each name on a new line |
|
eg .msgbox('Files padded. Thank you.\n\n' + names, 'python-padding.py') |
|
sys .exit() ## go ahead and close program at this point, because we have no file. |
|
|
|
elif howmany == 'Many' : ## pad all .py files in dir |
|
getpath(filename) |
|
|
|
for thisfile in os . listdir(path) : ## get a dir list |
|
getext(path + thisfile) |
|
if ext == 'py' : ## only look at .py files |
|
pad(path + thisfile) |
|
files .append(thisfile) ## add name to list, for reference |
|
names = '\n' .join(files) ## concatenate list, each name on a new line |
|
eg .msgbox('All .py files padded in\n\n' + str(path) + '\n\n' + names, 'python-padding.py') |
|
|
|
else : |
|
eg .msgbox('Nothing padded. Thank you.', 'python-padding.py') |
|
sys .exit() ## go ahead and close program |
|
|
|
|
|
##========================================================= |
|
## eof -------------------------------------------------- |