viernes, 24 de enero de 2025

Python (XXIII): "Camel Case" to "snake case": Script para cambiar todo un proyecto de nomenclatura

He instalado SonarQube Lint en VS y me ha llenado de "problemas" elcódigo pues megusta usar la nomenclatura "camel case" (camelCase) vez de "snake case" (snake_case) .

Debemos tener en cuenta que hay que respetar los comentarios, strings y expresiones regulares

Veamos el script que nos ha propuesto Chat GPT

import os
import re

def camel_to_snake(name):
    """Convierte un nombre de camelCase o PascalCase a snake_case."""
    # Reemplaza las mayúsculas precedidas por minúsculas o números por '_'
    name = re.sub(r'(?<=[a-z0-9])(?=[A-Z])', '_', name)
    # Reemplaza secuencias de mayúsculas seguidas de minúsculas (e.g., XMLParser -> xml_parser)
    name = re.sub(r'(?<=[A-Z])(?=[A-Z][a-z])', '_', name)
    return name.lower()

def process_file(file_path):
    """Procesa un archivo Python para reemplazar camelCase y PascalCase por snake_case."""
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()
    
    # Expresión regular para detectar cadenas, comentarios y regex
    pattern = r"""
        (['"]{3}.*?['"]{3})|          # Bloques de texto '''...''' o """..."""
        (['"].*?['"])|                # Strings simples '...' o "..."
        (#.*?$)|                      # Comentarios que comienzan con #
        (re\.compile\(\s*['"].*?['"]\))  # Expresiones regulares en re.compile(...)
    """
    
    # Función para preservar coincidencias
    def preserve_match(match):
        return match.group(0)  # Devuelve el bloque encontrado sin modificaciones

    # Expresión regular para encontrar nombres en camelCase o PascalCase
    camel_case_pattern = r'\b[a-zA-Z]+[A-Z][a-zA-Z]*\b'
    
    # Reemplaza fuera de strings, comentarios y regex
    def replace_non_protected(match):
        block = match.group(0)
        if match.group(0).startswith(('#', "'", '"', 're.compile(')):
            return preserve_match(match)
        else:
            return re.sub(camel_case_pattern, lambda m: camel_to_snake(m.group(0)), block)

    # Aplica el reemplazo
    updated_content = re.sub(
        pattern,
        replace_non_protected,
        content,
        flags=re.DOTALL | re.MULTILINE | re.VERBOSE
    )
    
    # Sobrescribe el archivo si hubo cambios
    if content != updated_content:
        with open(file_path, 'w', encoding='utf-8') as file:
            file.write(updated_content)
        print(f"Actualizado: {file_path}")

def process_directory(directory):
    """Procesa todos los archivos Python en un directorio recursivamente."""
    for root, _, files in os.walk(directory):
        for file in files:
            if file.endswith('.py'):  # Solo archivos Python
                process_file(os.path.join(root, file))

if __name__ == "__main__":
    project_directory = input("Introduce el directorio del proyecto: ").strip()
    process_directory(project_directory)
    print("Proceso completado.")









No hay comentarios :

Publicar un comentario