Skip to content

Instantly share code, notes, and snippets.

@doyousketch2
Last active January 17, 2017 20:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save doyousketch2/2411076a71eb0c8e1dd3a0c45d45cda7 to your computer and use it in GitHub Desktop.
Save doyousketch2/2411076a71eb0c8e1dd3a0c45d45cda7 to your computer and use it in GitHub Desktop.
Python script to add spacing to Python scripts.

OpenSource PythonVersions License Git.io

It's 2017:bangbang: New Year's resolution is:
1440 x 900 on my main monitor, and 1280 x 1024 on my secondary.

What it does:

Converts tabs to spaces, for posting on GitHub, so you don't get 8 spaces per tab every time...
Scoots comments to the right-margin so your code is easier to read.
You decide how far you'd like right-margin to be, user-set variable.
Adds spacing after commas, so tuples and lists are easier to digest.
Adds spacing before dots, to make functions easier to read.
Tidies up long strings of ####### with ##===== instead.
Adds spacing around math operators : < > = + -* / %
Double spaces around = signs.
Removes trailing spaces.

Img

What a Python file might look like before using script:

Before

It will ask how many files you wish to process:

One = pads one file.
Few = Will keep asking for a new file until you click "Cancel" or X.
Many = Choose a file, then it will pad all .py files in that directory.

'Prolly best to back up your Python files you wish to pad
before using this script, as it writes directly to each file.

It should be relatively harmless,
but you never know how it'll act with every version of Python out there.

Try it out on a few, and lemme know.
Let everyone else know you found it helpful.

#!/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 --------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment