Skip to content

Instantly share code, notes, and snippets.

@vvzen
Last active February 1, 2021 11:40
Show Gist options
  • Save vvzen/b024fc1a98ba906b48f3b5c5eb0d5bac to your computer and use it in GitHub Desktop.
Save vvzen/b024fc1a98ba906b48f3b5c5eb0d5bac to your computer and use it in GitHub Desktop.
Convert a python camelCase code base to snake_case
"""
This is currently highly experimental!!
Please make a backup before testing it..
"""
from __future__ import print_function
import os
import re
import sys
import json
import datetime
FUNCTION_REGEX = re.compile(r"( |\t)*def (?P<funcname>[_A-Za-z-0-9]*)")
def find_all_python_files(target_dir):
files_list = []
for root, folder, files in os.walk(target_dir):
for file in files:
if file.endswith(".py"):
files_list.append(os.path.join(root, file))
return files_list
def find_words_in_function_name(value):
current_word = []
words = []
for c in value:
# We have reached a new word
if c == "_" or c.upper() == c:
current_word = "".join(current_word)
if current_word:
words.append(current_word)
if c != "_":
current_word = [c]
else:
current_word = []
else:
if c and c != "_":
current_word.append(c)
current_word = "".join(current_word)
words.append(current_word)
return words
def get_all_functions(file_path, functions_map):
if not os.access(file_path, os.R_OK):
sys.stderr.write("Cannot read file %s\n" % file_path)
return
with open(file_path, "r") as f:
file_content = f.readlines()
for line in file_content:
matches = FUNCTION_REGEX.match(line.lstrip())
if not matches:
continue
function_name = matches.groupdict("funcname")["funcname"]
all_lower = function_name.lower()
if function_name == all_lower:
continue
words = find_words_in_function_name(function_name)
function_name = "%s(" % function_name
functions_map[function_name] = ("_".join(words)).lower() + "("
def replace_functions_in_file(file_path, functions_map, dry_run=True):
current_file_content = []
# Search...
with open(file_path, "r") as f:
file_content = f.readlines()
fix_all_header = False
for line in file_content:
for old_name, new_name in functions_map.iteritems():
# Deal with the top level __all__ "mask" that is used to
# only make some functions public
if "__all__" in line:
fix_all_header = True
# end of the __all__ (which can be potentially multiline)
if "]" in line and fix_all_header:
fix_all_header = False
if fix_all_header:
old_name_without_parenthesis = old_name.replace("(", "")
new_name_without_parenthesis = new_name.replace("(", "")
if old_name_without_parenthesis in line:
func_regex_pattern = r"\b%s\b" % old_name_without_parenthesis
for item in re.findall(func_regex_pattern, line):
line = line.replace(
item,
new_name_without_parenthesis
)
# Body of module.. nothing to worry about in theory
elif old_name in line:
line = line.replace(old_name, new_name)
current_file_content.append(line)
# ...and replace
if not dry_run:
with open(file_path, "w") as f:
f.write("".join(current_file_content))
def generate_report(functions_map):
timestamp = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%s")
report_file_name = "camelCase_to_snakeCase_%s.json" % timestamp
report_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), report_file_name)
with open(report_file_path, "w") as f:
json.dump(functions_map, f, indent=4)
def main():
functions_map = {}
dry_run = False
# target_file = "/Users/vvzen/Documents/code/pipeline/framestore/commsfcurve/commsfcurve/math/easing/filters.py"
# get_all_functions(target_file, functions_map)
# replace_functions_in_file(target_file, functions_map, dry_run=dry_run)
# target_file = "/Users/vvzen/Documents/code/pipeline/framestore/commsfcurve/commsfcurve/FCurve.py"
# get_all_functions(target_file, functions_map)
# replace_functions_in_file(target_file, functions_map, dry_run=dry_run)
target_dir = "/Users/vvzen/Documents/code/pipeline/framestore/commsfcurve"
python_files = find_all_python_files(target_dir)
# 1. Build a list of all the functions
for f in python_files:
get_all_functions(f, functions_map)
# 2. Convert to snake_case
for f in python_files:
replace_functions_in_file(f, functions_map, dry_run=dry_run)
generate_report(functions_map)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment