mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 08:21:36 +01:00 
			
		
		
		
	This is only for posterity, since once the conversion is done, the script is of no use. Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			181 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| # SPDX-License-Identifier: GPL-2.0+
 | |
| #
 | |
| # Copyright 2021 Google LLC
 | |
| #
 | |
| 
 | |
| """Changes the functions and class methods in a file to use snake case, updating
 | |
| other tools which use them"""
 | |
| 
 | |
| from argparse import ArgumentParser
 | |
| import glob
 | |
| import os
 | |
| import re
 | |
| import subprocess
 | |
| 
 | |
| import camel_case
 | |
| 
 | |
| # Exclude functions with these names
 | |
| EXCLUDE_NAMES = set(['setUp', 'tearDown', 'setUpClass', 'tearDownClass'])
 | |
| 
 | |
| # Find function definitions in a file
 | |
| RE_FUNC = re.compile(r' *def (\w+)\(')
 | |
| 
 | |
| # Where to find files that might call the file being converted
 | |
| FILES_GLOB = 'tools/**/*.py'
 | |
| 
 | |
| def collect_funcs(fname):
 | |
|     """Collect a list of functions in a file
 | |
| 
 | |
|     Args:
 | |
|         fname (str): Filename to read
 | |
| 
 | |
|     Returns:
 | |
|         tuple:
 | |
|             str: contents of file
 | |
|             list of str: List of function names
 | |
|     """
 | |
|     with open(fname, encoding='utf-8') as inf:
 | |
|         data = inf.read()
 | |
|         funcs = RE_FUNC.findall(data)
 | |
|     return data, funcs
 | |
| 
 | |
| def get_module_name(fname):
 | |
|     """Convert a filename to a module name
 | |
| 
 | |
|     Args:
 | |
|         fname (str): Filename to convert, e.g. 'tools/patman/command.py'
 | |
| 
 | |
|     Returns:
 | |
|         tuple:
 | |
|             str: Full module name, e.g. 'patman.command'
 | |
|             str: Leaf module name, e.g. 'command'
 | |
|             str: Program name, e.g. 'patman'
 | |
|     """
 | |
|     parts = os.path.splitext(fname)[0].split('/')[1:]
 | |
|     module_name = '.'.join(parts)
 | |
|     return module_name, parts[-1], parts[0]
 | |
| 
 | |
| def process_caller(data, conv, module_name, leaf):
 | |
|     """Process a file that might call another module
 | |
| 
 | |
|     This converts all the camel-case references in the provided file contents
 | |
|     with the corresponding snake-case references.
 | |
| 
 | |
|     Args:
 | |
|         data (str): Contents of file to convert
 | |
|         conv (dict): Identifies to convert
 | |
|             key: Current name in camel case, e.g. 'DoIt'
 | |
|             value: New name in snake case, e.g. 'do_it'
 | |
|         module_name: Name of module as referenced by the file, e.g.
 | |
|             'patman.command'
 | |
|         leaf: Leaf module name, e.g. 'command'
 | |
| 
 | |
|     Returns:
 | |
|         str: New file contents, or None if it was not modified
 | |
|     """
 | |
|     total = 0
 | |
| 
 | |
|     # Update any simple functions calls into the module
 | |
|     for name, new_name in conv.items():
 | |
|         newdata, count = re.subn(fr'{leaf}.{name}\(',
 | |
|                                  f'{leaf}.{new_name}(', data)
 | |
|         total += count
 | |
|         data = newdata
 | |
| 
 | |
|     # Deal with files that import symbols individually
 | |
|     imports = re.findall(fr'from {module_name} import (.*)\n', data)
 | |
|     for item in imports:
 | |
|         #print('item', item)
 | |
|         names = [n.strip() for n in item.split(',')]
 | |
|         new_names = [conv.get(n) or n for n in names]
 | |
|         new_line = f"from {module_name} import {', '.join(new_names)}\n"
 | |
|         data = re.sub(fr'from {module_name} import (.*)\n', new_line, data)
 | |
|         for name in names:
 | |
|             new_name = conv.get(name)
 | |
|             if new_name:
 | |
|                 newdata = re.sub(fr'\b{name}\(', f'{new_name}(', data)
 | |
|                 data = newdata
 | |
| 
 | |
|     # Deal with mocks like:
 | |
|     # unittest.mock.patch.object(module, 'Function', ...
 | |
|     for name, new_name in conv.items():
 | |
|         newdata, count = re.subn(fr"{leaf}, '{name}'",
 | |
|                                  f"{leaf}, '{new_name}'", data)
 | |
|         total += count
 | |
|         data = newdata
 | |
| 
 | |
|     if total or imports:
 | |
|         return data
 | |
|     return None
 | |
| 
 | |
| def process_file(srcfile, do_write, commit):
 | |
|     """Process a file to rename its camel-case functions
 | |
| 
 | |
|     This renames the class methods and functions in a file so that they use
 | |
|     snake case. Then it updates other modules that call those functions.
 | |
| 
 | |
|     Args:
 | |
|         srcfile (str): Filename to process
 | |
|         do_write (bool): True to write back to files, False to do a dry run
 | |
|         commit (bool): True to create a commit with the changes
 | |
|     """
 | |
|     data, funcs = collect_funcs(srcfile)
 | |
|     module_name, leaf, prog = get_module_name(srcfile)
 | |
|     #print('module_name', module_name)
 | |
|     #print(len(funcs))
 | |
|     #print(funcs[0])
 | |
|     conv = {}
 | |
|     for name in funcs:
 | |
|         if name not in EXCLUDE_NAMES:
 | |
|             conv[name] = camel_case.to_snake(name)
 | |
| 
 | |
|     # Convert name to new_name in the file
 | |
|     for name, new_name in conv.items():
 | |
|         #print(name, new_name)
 | |
|         # Don't match if it is preceded by a '.', since that indicates that
 | |
|         # it is calling this same function name but in a different module
 | |
|         newdata = re.sub(fr'(?<!\.){name}\(', f'{new_name}(', data)
 | |
|         data = newdata
 | |
| 
 | |
|         # But do allow self.xxx
 | |
|         newdata = re.sub(fr'self.{name}\(', f'self.{new_name}(', data)
 | |
|         data = newdata
 | |
|     if do_write:
 | |
|         with open(srcfile, 'w', encoding='utf-8') as out:
 | |
|             out.write(data)
 | |
| 
 | |
|     # Now find all files which use these functions and update them
 | |
|     for fname in glob.glob(FILES_GLOB, recursive=True):
 | |
|         with open(fname, encoding='utf-8') as inf:
 | |
|             data = inf.read()
 | |
|         newdata = process_caller(fname, conv, module_name, leaf)
 | |
|         if do_write and newdata:
 | |
|             with open(fname, 'w', encoding='utf-8') as out:
 | |
|                 out.write(newdata)
 | |
| 
 | |
|     if commit:
 | |
|         subprocess.call(['git', 'add', '-u'])
 | |
|         subprocess.call([
 | |
|             'git', 'commit', '-s', '-m',
 | |
|             f'''{prog}: Convert camel case in {os.path.basename(srcfile)}
 | |
| 
 | |
| Convert this file to snake case and update all files which use it.
 | |
| '''])
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     """Main program"""
 | |
|     epilog = 'Convert camel case function names to snake in a file and callers'
 | |
|     parser = ArgumentParser(epilog=epilog)
 | |
|     parser.add_argument('-c', '--commit', action='store_true',
 | |
|                         help='Add a commit with the changes')
 | |
|     parser.add_argument('-n', '--dry_run', action='store_true',
 | |
|                         help='Dry run, do not write back to files')
 | |
|     parser.add_argument('-s', '--srcfile', type=str, required=True, help='Filename to convert')
 | |
|     args = parser.parse_args()
 | |
|     process_file(args.srcfile, not args.dry_run, args.commit)
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main()
 |