Last active
January 16, 2023 11:32
-
-
Save un1tz3r0/9ee6e01ba6ce022b003a825e3a9dbd8a to your computer and use it in GitHub Desktop.
reindent.py: Fix Python Indent Errors
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
''' | |
reindent.py | |
this script is a tool for fixing inconsistent indentation when your chosen editor chooses to hose your tabs with a bunch of spaces | |
''' | |
import os, sys, argparse | |
from io import StringIO | |
def reindent(buffer, tabs=2, tospaces=False, nospaces=True, fromtabs=None, backupsuffix="~"): | |
if fromtabs == None: | |
fromtabs = tabs | |
outlines = [] | |
with StringIO(buffer) as fin: | |
buf = fin.read() | |
lines = buf.splitlines() | |
for lineno, line in enumerate(lines): | |
line = line.rstrip() | |
indent = 0 | |
pos = 0 | |
while pos < len(line) and line[pos] in [' ', '\t']: | |
if line[pos] == ' ': | |
indent = indent + 1 | |
elif line[pos] == '\t': | |
indent = (indent//fromtabs + 1) * fromtabs | |
else: | |
break | |
pos = pos + 1 | |
line = line[pos:] | |
if indent - (indent//tabs)*tabs > 0: | |
if nospaces and not tospaces: | |
#line = '\t' + line | |
raise RuntimeError(f"[line {lineno}] Indentation does not line up with tab-stops, aborting.") | |
else: | |
line = (' '*(indent - (indent//tabs)*tabs)) + line | |
for i in range(0, indent//tabs): | |
if tospaces: | |
line = (' '*tabs) + line | |
else: | |
line = '\t' + line | |
outlines.append(line) | |
return "\n".join(outlines) | |
def backup_file(filename, backupsuffix="~", nobackup=False, overwriteok=True): | |
try: | |
import os | |
outputfilename = filename | |
if not nobackup and os.path.exists(outputfilename): | |
suff = 0 | |
backupfilename = f"{outputfilename}{backupsuffix}" | |
while os.path.exists(backupfilename): | |
suff = suff + 1 | |
backupfilename = f"{outputfilename}{backupsuffix}{suff}" | |
#print(f"backing up existing output file {outputfilename}.py as {backupfilename}...") | |
#overwriteok = False | |
with open(outputfilename, "rb") as fin: | |
buf = fin.read() | |
with open(backupfilename, "wb") as fout: | |
fout.write(buf) | |
#print(f"... wrote {len(buf)} bytes to backup file.") | |
except Exception as err: | |
print(f"Backing up original file failed: {err}") | |
return False | |
return True | |
def reindent_file(filename, backupsuffix="~", backupcount=None, *args, **kwargs): | |
import os | |
with open(filename, "r") as finput: | |
buffer = finput.read() | |
result = reindent(buffer, *args, **kwargs) | |
if not backup_file(filename, backupsuffix = backupsuffix, nobackup = backupcount==0): | |
raise RuntimeError(f"Failed to backup original input file, refusing to overwrite: {filename}") | |
with open(filename, "w") as fout: | |
written = fout.write(result) | |
return True, len(buffer), written | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="Fix in-place python scripts with inconsistent indentation") | |
parser.add_argument("-t", "--tabs", type=int, default="2", help="Output tab width in columns. Default is 2.") | |
parser.add_argument("-s", "--space", action="store_true", help="Indent with spaces instead of tabs") | |
parser.add_argument("-m", "--mixed", action="store_true", help="Allow indentation between tab stops by padding with spaces.") | |
parser.add_argument("-f", "--from", dest="from_tabs", action="store", type=int, default=None, help="Tab size in input to convert from. Defaults to -t output tab size.") | |
parser.add_argument("-b", "--backup-suffix", action="store", type=str, default="~", help="Backup filename suffix") | |
parser.add_argument("-n", "--backup-count", action="store", type=str, default=-1, help="Maximum number of backups to keep. If negative, removes the oldest backup when count is reached. If positive, rotate backups and remove the oldest.") | |
parser.add_argument("-c", "--cleanup", action="store_true", default=False, help="Automatically detect indented blocks in python mode and reindent each level by one block") | |
parser.add_argument("filename", nargs="+", type=str, help="The file to reindent") | |
opts = parser.parse_args() | |
for fn in opts.filename: | |
print(f"Reindenting {repr(fn)}...") | |
reindent_file(fn, backupsuffix=opts.backup_suffix, backupcount=opts.backup_count, tabs=opts.tabs, tospaces=opts.space, nospaces=not opts.mixed, fromtabs=opts.from_tabs) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment