
Una plantilla para gobernarlos a todos: gestionar proyectos Python con Cookiecutter y Cruft
/ 5 min de lectura
Índice
Crea proyectos más rápido y mantenlos coherentes
Empezar un proyecto Python nuevo a menudo significa repetir el mismo trabajo de configuración: crear un Dockerfile, configurar Poetry, organizar las carpetas, añadir reglas de linting y conectar las herramientas de desarrollo. Algunos proyectos necesitan pequeñas variaciones, pero la mayor parte de la estructura suele ser la misma.
Hacerlo a mano cada vez es lento y es fácil equivocarse. Ahí es donde ayuda Cookiecutter. Y cuando tienes muchos proyectos generados a partir de la misma plantilla, Cruft te ayuda a mantenerlos actualizados.
¿Qué es Cookiecutter?
Cookiecutter es una herramienta de Python para generar proyectos a partir de plantillas con la sintaxis de Jinja. Aunque es un paquete de Python, puedes usarlo para crear la estructura de proyectos en muchos lenguajes.
Crear proyectos con Cookiecutter
Hay dos formas habituales de usar Cookiecutter:
- Instalar y usar una plantilla existente.
- Construir tu propia plantilla.
Este artículo se centra en la segunda opción.
Para instalar Cookiecutter, ejecuta uno de estos comandos:
poetry add cookiecutterpip install cookiecutterconda install -c conda-forge cookiecutterUna vez instalado, puedes empezar a construir tu plantilla.
Estructura típica de un proyecto
Aquí tienes un ejemplo de la estructura de proyecto Python que reutilizo a menudo:
Esta estructura me permite crear un proyecto nuevo en segundos. Incluye:
- Una carpeta
packageconscriptsyutils, donde vive la lógica de la aplicación. - Una carpeta
.devcontainerpara la configuración de Docker y del contenedor de desarrollo. - Una carpeta
configpara los archivos de configuración. - Una carpeta
datapara conjuntos de datos, imágenes y archivos de texto. - Archivos a nivel de repositorio como
pyproject.toml,.pre-commit-config.yamly los archivos de ignore.
Estos elementos aparecen en muchos de mis proyectos, y la mayor parte de su configuración es compartida.
Construir una plantilla de Cookiecutter propia
Para crear una plantilla, añade un archivo cookiecutter.json al repositorio de la plantilla. Este archivo define los valores que Cookiecutter te pedirá al generar un proyecto nuevo.
{ "project_name": "Name of the project", "project_slug": "Slug of the project", "package_name": "Name of the package", "repository_url": "URL of the project", "project_description": "Description of the project"}Este ejemplo es sencillo a propósito, pero basta para muchos proyectos. Cada valor hace de valor por defecto de la pregunta que se muestra al crear el proyecto.
Cookiecutter usa la sintaxis de Jinja, así que puedes hacer referencia a los valores así:
{{ cookiecutter.project_slug }}En mi caso, la plantilla usa carpetas como {{ cookiecutter.project_slug }} y {{ cookiecutter.project_name }}. Todo lo que hay dentro de esas carpetas pasa a formar parte del proyecto generado.
Mi plantilla de Cookiecutter, project-wizard, es así:
Por ejemplo, genero automáticamente un README a partir de los valores definidos en cookiecutter.json:
# {{ cookiecutter.project_name }}
{{ cookiecutter.project_description }}En archivos como pyproject.toml también defino los paquetes y las herramientas que quiero en cada repositorio nuevo. La carpeta scripts puede incluir scripts de utilidad que reutilizo a menudo. Es aquí donde Cookiecutter empieza a ahorrarte tiempo de verdad.
Crear un proyecto nuevo
Cuando la plantilla esté lista, genera un proyecto con:
cookiecutter .Si la plantilla está en GitHub, también puedes ejecutar:
cookiecutter https://github.com/simoncraf/project-wizard.gitCookiecutter te pedirá entonces los valores definidos en cookiecutter.json. Los valores por defecto aparecen entre corchetes.
A partir de aquí puedes crear tantos proyectos como quieras desde la misma plantilla. El siguiente problema es el mantenimiento: cuando la plantilla evoluciona, mantener alineados todos los proyectos generados se vuelve tedioso. Ese es el problema que resuelve Cruft.
¿Qué es Cruft?
Cruft es un paquete de Python que mantiene los proyectos generados sincronizados con la plantilla de Cookiecutter de la que provienen.
Vincular un proyecto con Cruft
Empieza instalando Cruft:
poetry add cruftpip install cruftconda install -c conda-forge cruftDespués añade un archivo .cruft.json a tu plantilla de Cookiecutter:
{ "template": "https://github.com/simoncraf/project-wizard", "commit": "069a78495a5d0cbffc8245f42bd190adaa1df698", "checkout": "null", "context": { "cookiecutter": { "project_name": "{{cookiecutter.project_name }}", "project_slug": "{{cookiecutter.project_slug }}", "package_name": "{{cookiecutter.package_name }}", "project_short_description": "{{cookiecutter.project_description }}", "repository_url": "{{cookiecutter.repository_url }}", "_template": "https://github.com/simoncraf/project-wizard" } }, "directory": null}Este archivo guarda la información que Cruft necesita para seguir la plantilla. Los dos campos más importantes son:
template, que apunta al repositorio de la plantilla de Cookiecutter.commit, que identifica la versión de la plantilla que usa el proyecto.
Cuando la plantilla cambia, también cambia su último commit. Cruft compara el commit registrado en el proyecto con el de la plantilla y te avisa cuando hay una actualización disponible.
En mi plantilla también uso un script auxiliar para asegurarme de que el commit guardado en .cruft.json coincide con el último commit de la plantilla:
def get_last_commit_hash(): """ Update the commit hash in `.cruft.json` to the latest commit. """ result = subprocess.run(["git", "log", "-n", "1", "--format=%H"], stdout=subprocess.PIPE) hash_ = result.stdout.decode().strip() file_path = Path("{{cookiecutter.project_slug}}") / ".cruft.json" with file_path.open("r", encoding="utf-8") as f: data = json.load(f) data["commit"] = hash_ with file_path.open("w", encoding="utf-8") as f: json.dump(data, f, indent=4) return hash_Si ya has creado un proyecto con Cookiecutter pero no incluye .cruft.json, puedes vincularlo manualmente:
cruft link https://github.com/simoncraf/project-wizardPor defecto, esto vincula el proyecto al último commit de la plantilla y te crea el archivo .cruft.json.
Actualizar un proyecto
Una vez vinculado el proyecto, comprueba si hay actualizaciones con:
cruft checkSi no las hay, verás un mensaje de éxito:
Si la plantilla ha cambiado, Cruft te mostrará que hay una actualización disponible:
Para aplicar las actualizaciones de la plantilla, ejecuta:
cruft updateCruft te permite revisar los cambios, saltártelos o aplicarlos directamente. Después de aplicarlos, reescribe automáticamente los archivos correspondientes.
Si hay conflictos, Cruft crea archivos .rej. Funcionan de forma parecida a los rechazos de los parches y te ayudan a revisar lo que no se ha podido fusionar automáticamente. Cuando resuelves o descartas los cambios, el campo commit de .cruft.json se actualiza a la última versión de la plantilla.
Recursos
Enlaces útiles:
Plantillas de Cookiecutter listas para usar que vale la pena explorar: