Last active
October 28, 2024 10:54
-
-
Save nitori/2d666af856215d28d12fecd0f36daa68 to your computer and use it in GitHub Desktop.
Convert python identifiers to some preselected Unicode characters
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
import ast | |
import os | |
import random | |
import string | |
import sys | |
import unicodedata as ud | |
class IdentifierTransformer(ast.NodeTransformer): | |
def __init__(self, char_sets: dict[str, dict[str, list]]): | |
self.char_sets = char_sets | |
self._replaced_names = {} | |
def update_name(self, name: str): | |
if name in self._replaced_names: | |
return self._replaced_names[name] | |
lower_set_to_use = random.choice(list(self.char_sets['lower'])) | |
upper_set_to_use = lower_set_to_use | |
# try to use the same set for upper case if available | |
if upper_set_to_use not in self.char_sets['upper']: | |
upper_set_to_use = random.choice(list(self.char_sets['upper'])) | |
new_name = '' | |
for char in name: | |
if char in string.ascii_lowercase: | |
i = string.ascii_lowercase.index(char) | |
new_name += self.char_sets['lower'][lower_set_to_use][i] | |
elif char in string.ascii_uppercase: | |
i = string.ascii_uppercase.index(char) | |
new_name += self.char_sets['upper'][upper_set_to_use][i] | |
else: | |
new_name += char | |
self._replaced_names[name] = new_name | |
return new_name | |
def visit_ImportFrom(self, node: ast.ImportFrom): | |
for alias in node.names: | |
alias.name = self.update_name(alias.name) | |
if node.module is not None: | |
node.module = self.update_name(node.module) | |
return self.generic_visit(node) | |
def visit_Import(self, node: ast.Import): | |
for alias in node.names: | |
alias.name = self.update_name(alias.name) | |
return self.generic_visit(node) | |
def visit_Attribute(self, node: ast.Attribute): | |
node.attr = self.update_name(node.attr) | |
return self.generic_visit(node) | |
def visit_Name(self, node: ast.Name): | |
node.id = self.update_name(node.id) | |
return self.generic_visit(node) | |
def visit_FunctionDef(self, node: ast.FunctionDef): | |
node.name = self.update_name(node.name) | |
return self.generic_visit(node) | |
def visit_ClassDef(self, node: ast.ClassDef): | |
node.name = self.update_name(node.name) | |
return self.generic_visit(node) | |
def visit_arg(self, node: ast.arg): | |
node.arg = self.update_name(node.arg) | |
return self.generic_visit(node) | |
def main(): | |
if len(sys.argv) != 3: | |
print(f'Usage: python {sys.argv[0]} <input_file> <output_file>') | |
return | |
char_sets: dict[str, dict[str, list]] = { | |
'lower': { | |
'math1': [], | |
'math2': [], | |
'math3': [], | |
'math4': [], | |
}, | |
'upper': { | |
'math1': [], | |
'math2': [], | |
'math3': [], | |
}, | |
} | |
for letter in string.ascii_uppercase: | |
char_sets['lower']['math1'].append(ud.lookup(f'MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL {letter}')) | |
char_sets['lower']['math2'].append(ud.lookup(f'MATHEMATICAL SANS-SERIF BOLD SMALL {letter}')) | |
char_sets['lower']['math3'].append(ud.lookup(f'MATHEMATICAL SANS-SERIF ITALIC SMALL {letter}')) | |
char_sets['lower']['math4'].append(ud.lookup(f'MATHEMATICAL FRAKTUR SMALL {letter}')) | |
char_sets['upper']['math1'].append(ud.lookup(f'MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL {letter}')) | |
char_sets['upper']['math2'].append(ud.lookup(f'MATHEMATICAL SANS-SERIF BOLD CAPITAL {letter}')) | |
char_sets['upper']['math3'].append(ud.lookup(f'MATHEMATICAL SANS-SERIF ITALIC CAPITAL {letter}')) | |
# not all exist for FRAKTUR it seems... sad | |
# char_sets['upper']['math4'].append(ud.lookup(f'MATHEMATICAL FRAKTUR CAPITAL {letter}')) | |
with open(sys.argv[1], 'r', encoding='utf-8') as f: | |
code = f.read() | |
tree = ast.parse(code) | |
new_tree = ast.fix_missing_locations(IdentifierTransformer(char_sets).visit(tree)) | |
if os.path.exists(sys.argv[2]): | |
if input(f'File {sys.argv[2]} exists. Overwrite? [y/N] ').lower() != 'y': | |
print('Aborting.') | |
return | |
with open(sys.argv[2], 'w', encoding='utf-8') as f: | |
f.write(ast.unparse(new_tree)) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Applied on this: https://gist.github.com/nitori/e9704b249526296c1e5701cd9822cbf7