¿Por qué no hay Makefiles para la automatización en proyectos Python?
automation rake (7)
Como programador de Python desde hace mucho tiempo, me pregunto si un aspecto central de la cultura de Python me eludió durante mucho tiempo: ¿Qué hacemos en lugar de Makefiles?
La mayoría de los proyectos ruby que he visto (no solo los rieles) utilizan Rake , poco después de que node.js se hizo popular, había pastel . En muchos otros (no solo lenguajes completos) hay archivos de creación clásicos.
Pero en Python, nadie parece necesitar tal infraestructura. Escogí al azar proyectos de Python en GitHub, y no tenían automatización, además de la instalación, proporcionada por setup.py
.
¿Cuál es la razón detrás de esto?
¿No hay nada que automatizar? ¿La mayoría de los programadores prefieren ejecutar chequeos de estilo, pruebas, etc. manualmente?
Algunos ejemplos:
-
dependecies
configura un virtualenv e instala las dependencias -
check
llama apep8
ypylint
commandlinetools. - la tarea de
test
depende de lasdependencies
habilita el virtualenv, inicia selenium-server para las pruebas de integración, y llama anosetest
- la tarea de
coffeescript
compila todos los coffeescripts a javascript minificado - La tarea
runserver
depende dedependencies
ycoffeescript
- la tarea de
deploy
depende de lacheck
ytest
y despliega el proyecto. - La tarea
docs
llama a la esfinge con los argumentos apropiados.
Algunos de ellos son solo de una o dos líneas, pero imho, se suman. Debido al Makefile, no tengo que recordarlos.
Para aclarar: no estoy buscando un equivalente de Python para Rake. Me alegro con la pavimentadora. Estoy buscando las razones.
¿No hay nada que automatizar?
Realmente no. Todos menos dos de los ejemplos son comandos de una línea.
tl; dr Muy poco de esto es realmente interesante o complejo. Muy poco de esto parece beneficiarse de la "automatización".
Debido a la documentación, no tengo que recordar los comandos para hacer esto.
¿La mayoría de los programadores prefieren ejecutar chequeos de estilo, pruebas, etc. manualmente?
Sí.
documentación de generación, la tarea docs llama a la esfinge con los argumentos apropiados
Es una línea de código. La automatización no ayuda mucho. sphinx-build -b html source build/html
. Eso es un guión. Escrito en pitón.
Hacemos esto raramente. Algunas veces a la semana. Después de los cambios "significativos".
ejecutando stylechecks (Pylint, Pyflakes y pep8-cmdtool). check llama a pep8 y pylint commandlinetools
Nosotros no hacemos esto Usamos pruebas unitarias en lugar de pylint. Podrías automatizar ese proceso de tres pasos.
Pero puedo ver cómo SCons o Make podrían ayudar a alguien aquí.
pruebas
Puede haber espacio para la "automatización" aquí. Son dos líneas: las pruebas unitarias que no son de Django ( python test/main.py
) y las pruebas de Django. ( manage.py test
). La automatización se podría aplicar para ejecutar ambas líneas.
Hacemos esto docenas de veces cada día. Nunca supimos que necesitábamos "automatización".
dependecies configura un virtualenv e instala las dependencias
Hecho tan raramente que una simple lista de pasos es todo lo que siempre hemos necesitado. Hacemos un seguimiento muy cuidadoso de nuestras dependencias, por lo que nunca hay sorpresas.
Nosotros no hacemos esto
la tarea de prueba depende de las dependencias habilita el virtualenv, inicia selenium-server para las pruebas de integración, y llama a nosetest
El start server & run nosetest
como una "automatización" de dos pasos tiene sentido. Le evita ingresar los dos comandos de shell para ejecutar ambos pasos.
la tarea de coffeescript compila todos los coffeescripts a javascript minificado
Esto es algo que es muy raro para nosotros. Supongo que es un buen ejemplo de algo para ser automatizado. Automatizar el script de una línea podría ser útil.
Puedo ver cómo SCons o Make podrían ayudar a alguien aquí.
La tarea RunServer depende de dependencias y coffeescript.
Excepto. Las dependencias cambian tan raramente, que esto parece una exageración. Supongo que puede ser una buena idea de que no estás rastreando bien las dependencias.
la tarea de implementación depende de la verificación y prueba y despliega el proyecto.
Es una instalación svn co
y python setup.py install
en el servidor, seguida de un grupo de copias específicas del cliente desde el área de subversión al área del cliente /www
. Eso es un guión. Escrito en pitón.
No es una marca general o algo así como SCons. Tiene un solo actor (un administrador de sistemas) y un caso de uso. Nunca mezclaríamos la implementación con otras tareas de desarrollo, control de calidad o pruebas.
Cualquier herramienta de prueba decente tiene una forma de ejecutar la suite completa en un solo comando, y nada le impide usar rake, make, o cualquier otra cosa, realmente.
Hay pocas razones para inventar una nueva forma de hacer las cosas cuando los métodos existentes funcionan perfectamente bien. ¿Por qué reinventar algo simplemente porque USTED no lo inventó? (NIH).
El PEP original donde se planteó esto se puede encontrar here . Distutils se ha convertido en el método estándar para distribuir e instalar módulos de Python.
¿Por qué? Simplemente sucede que python es un lenguaje maravilloso para realizar la instalación de los módulos de Python.
En realidad, la automatización también es útil para los desarrolladores de Python.
Invocar es probablemente la herramienta más cercana a lo que tiene en mente, para la automatización de tareas comunes repetitivas de Python: https://github.com/pyinvoke/invoke
Con la invocación, puede crear un task.py como este (tomado de los documentos de invocación)
from invoke import run, task
@task
def clean(docs=False, bytecode=False, extra=''''):
patterns = [''build'']
if docs:
patterns.append(''docs/_build'')
if bytecode:
patterns.append(''**/*.pyc'')
if extra:
patterns.append(extra)
for pattern in patterns:
run("rm -rf %s" % pattern)
@task
def build(docs=False):
run("python setup.py build")
if docs:
run("sphinx-build docs docs/_build")
A continuación, puede ejecutar las tareas en la línea de comandos, por ejemplo:
$ invoke clean
$ invoke build --docs
Otra opción es simplemente usar un Makefile. Por ejemplo, el Makefile de un proyecto Python podría verse así:
docs:
$(MAKE) -C docs clean
$(MAKE) -C docs html
open docs/_build/html/index.html
release: clean
python setup.py sdist upload
sdist: clean
python setup.py sdist
ls -l dist
Hay una serie de opciones para la automatización en Python. No creo que haya una cultura contra la automatización, simplemente no hay una forma dominante de hacerlo. El denominador común es distutils .
El que está cerrado a su descripción es buildout . Esto se utiliza principalmente en el mundo Zope / Plone.
Yo mismo uso una combinación de los siguientes: Distribute , pip y Fabric . Estoy desarrollando principalmente utilizando Django que tiene manage.py para comandos de automatización.
También se está trabajando activamente en Python 3.3
Los lenguajes dinámicos no necesitan nada como make
, a menos que tengan algunas dependencias de interfaz de tiempo de compilación entre los módulos. Para eso, Python tendría que obtener macros. Necesita make
en C para hacer una reconstrucción incremental cuando las interfaces cambian, porque los cambios en elementos como firmas de funciones o diseños de estructura significan que el código binario compilado previamente ya no es válido.
Tenga en cuenta que las dependencias se utilizan solo para recompilar de forma mínima los programas de C. Cuando reconstruyes un programa en C con un Makefile típico, la vinculación se realiza completamente. Todos los archivos .o
, ya sea que se hayan recompilado recientemente o si son viejos de una compilación anterior, se cargan en la memoria y se convierten en una imagen.
En lenguajes dinámicos, la construcción de programas se asemeja más al paso de vinculación de los programas en C que al paso de compilación.
Para hacer el programa dinámico, tiene que cargar todo, y si el lenguaje dinámico está compilado, la recompilación es generalmente simple: si el archivo fuente es más nuevo que el compilado, entonces vuelva a compilarlo.
Lo único que make
que sobresalga, y por qué se utiliza, son las compilaciones incrementales con grandes dependencias de múltiples capas. Incluso para lenguajes compilados como C, no usaríamos make
si no nos importaran las reconstrucciones incrementales . Es decir, si el programa se reconstruía desde cero cada vez que se realizaba un cambio, los pasos de compilación con script podrían producir la imagen.
En un proyecto de C, make se usa para otras tareas de automatización, porque ya se usa para construir el programa. El Makefile
simplemente "hace crecer el cabello": acumula varios objetivos ad hoc que realizan varias tareas livianas. Ocurre porque Makefile
maneja los problemas más complicados y detallados de construir el programa, de manera incremental y desde cero. Crea una especie de "hub" para organizar otras automatizaciones. Esos objetivos no ejercen lo que hace la marca; podrían fácilmente ser scripts de shell fuera del Makefile
. La mayoría de estas actividades no se ajustan al paradigma de make de actualizar un objetivo en función de su falta, o los requisitos previos son más nuevos. Muchos de ellos ejecutan una acción incondicionalmente, por ejemplo: actualiza el objetivo falso dos veces y el efecto ocurre dos veces. (Una regla verdadera de make no hará nada la segunda vez si el primer intento logra actualizar su objetivo).
Una razón técnica por la que un Makefile
termina como el centro de automatización en algunos proyectos de C es que el sistema de configuración de compilación prepara algunas variables importantes, y éstas están en sintaxis de creación, que aparecen como asignaciones en algún archivo de inclusión de marca. Por supuesto, cuando necesita automatizar algo, la secuencia de comandos necesita el contenido de estas variables y, por lo tanto, lo coloca donde tenga acceso a ellas: en el Makefile
como nuevo objetivo.
Si esta práctica ocurriera en una situación en la que nunca se usó make para realizar compilaciones incrementales en un árbol de archivos de objetos, sería un "anti-patrón". Cualquiera de los codificadores de Python está evitando instintivamente el anti-patrón, o simplemente no se les ocurre que en lugar de escribir cinco scripts simples para hacer cinco tareas de automatización, podrían crear un Makefile
con cinco objetivos falsos, y se divertirán aún más tratando las idiosincrasias como cada línea de una receta de marca que se ejecuta en una nueva instancia de shell.
Setuptools
puede automatizar muchas cosas, y para las cosas que no están integradas, es fácilmente extensible.
- Para ejecutar unittests, puede usar el comando
setup.py test
después de haber agregado un argumentotest_suite
a la llamada asetup()
. ( documentation ) - Las dependencias (incluso si no están disponibles en PyPI) se pueden manejar agregando un argumento
install_requires
/extras_require
/dependency_links
a la llamada asetup()
. ( documentation ) - Para crear un paquete
.deb
, puede usar el módulostdeb
. - Para todo lo demás, puede agregar comandos personalizados setup.py .
Pero estoy de acuerdo con S.Lott
, la mayoría de las tareas que desearía automatizar (excepto el manejo de dependencias tal vez, es la única que me parece realmente útil) son tareas que no se ejecutan todos los días, por lo que no habría ningún problema real. Mejora de la productividad al automatizarlos.