generate_camel_case_aliases.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. #!/usr/bin/env python3
  2. """Generate camel case aliases for snake case functions."""
  3. import ast
  4. import os
  5. SOURCE_FOLDER = 'yandex_music'
  6. EXCLUDED_FUNCTIONS = {'de_dict', 'de_json', 'de_list', 'de_json_async', 'de_list_async'}
  7. ALIAS_TEMPLATE = """
  8. #: Псевдоним для :attr:`{name}`
  9. {camel_case_name} = {name}
  10. """
  11. ALIAS_SECTION_MARKER = ' # camelCase псевдонимы'
  12. def _validate_function_name(function_name: str) -> bool:
  13. if function_name.startswith('_'):
  14. return False
  15. if function_name in EXCLUDED_FUNCTIONS:
  16. return False
  17. # camel case will be the same
  18. if '_' not in function_name:
  19. return False
  20. return True
  21. def convert_snake_case_to_camel_case(string: str) -> str:
  22. """Convert snake case string to camel case string."""
  23. camel_case = ''.join(word.title() for word in string.split('_'))
  24. return camel_case[0].lower() + camel_case[1:]
  25. def _generate_code(function_name: str, intent: int = 0) -> str:
  26. camel_case_name = convert_snake_case_to_camel_case(function_name)
  27. code = ALIAS_TEMPLATE.format(name=function_name, camel_case_name=camel_case_name)
  28. code_lines = [line for line in code.split('\n') if line]
  29. code_lines = [f'{" " * intent}{line}' for line in code_lines]
  30. return '\n'.join(code_lines)
  31. def _process_file(file: str) -> None:
  32. with open(file, 'r', encoding='UTF-8') as f:
  33. count_of_class_def = 0
  34. file_aliases_code_fragments = []
  35. tree = ast.parse(f.read())
  36. for node in ast.walk(tree):
  37. if isinstance(node, ast.ClassDef):
  38. count_of_class_def += 1
  39. if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and _validate_function_name(node.name):
  40. alias_code = _generate_code(node.name, node.col_offset)
  41. file_aliases_code_fragments.append(alias_code)
  42. # there are no such cases in data models yet
  43. # only in yandex_music/exceptions.py and yandex_music/utils/difference.py
  44. if count_of_class_def != 1:
  45. return
  46. f.seek(0)
  47. file_code_lines = f.read().splitlines()
  48. marker_lineno = None
  49. for lineno, code_line in enumerate(file_code_lines):
  50. if code_line == ALIAS_SECTION_MARKER:
  51. marker_lineno = lineno
  52. break
  53. # we can't process files without markers now
  54. if marker_lineno is None:
  55. return
  56. # remove prev aliases
  57. file_code_lines = file_code_lines[: marker_lineno + 1]
  58. file_code_lines.append('')
  59. file_code_lines.extend(file_aliases_code_fragments)
  60. file_code_lines.append('')
  61. new_file_code = '\n'.join(file_code_lines)
  62. with open(file, 'w', encoding='UTF-8') as f:
  63. f.write(new_file_code)
  64. def main() -> None:
  65. """Generate camel case aliases for snake case functions."""
  66. for root, _, files in os.walk(SOURCE_FOLDER):
  67. for file in files:
  68. if file.endswith('.py') and file != '__init__.py':
  69. filepath = os.path.join(root, file)
  70. _process_file(filepath)
  71. if __name__ == '__main__':
  72. main()