Siempre he usado archivos JSON para la configuración de mis aplicaciones. Comencé a usarlos desde que codifiqué mucho Java, y ahora estoy trabajando principalmente en el desarrollo de Python del lado del servidor y de la ciencia de datos y no estoy seguro de si JSON es el camino correcto.
He visto a Celery usar archivos reales de Python para la configuración. Inicialmente era escéptico al respecto. Pero la idea de usar estructuras de datos Python simples para la configuración está comenzando a crecer en mí. Algunos pros:
- Las estructuras de datos serán las mismas que normalmente estoy codificando. Por lo tanto, no necesito cambiar el estado de ánimo.
- Mi IDE (PyCharm) comprende la conexión entre la configuración y el código. Ctrl+ Bpermite saltar fácilmente entre la configuración y el código.
- No necesito trabajar con IMO innecesaria estricta JSON . Estoy viendo comillas dobles, sin comas y sin comentarios.
- Puedo escribir configuraciones de prueba en la aplicación en la que estoy trabajando, luego transferirlas fácilmente a un archivo de configuración sin tener que hacer ninguna conversión y análisis JSON.
- Es posible hacer scripts muy simples en el archivo de configuración si es realmente necesario. (Aunque esto debería ser muy, muy limitado).
Entonces, mi pregunta es: si cambio, ¿cómo me estoy disparando en el pie?
Ningún usuario final no calificado utilizará los archivos de configuración. Cualquier cambio en los archivos de configuración está actualmente comprometido con Git y se implementa en nuestros servidores como parte de la implementación continua. No hay cambios de configuración manuales, a menos que haya una emergencia o esté en desarrollo.
(He considerado YAML , pero algo me molesta. Entonces, por ahora está fuera de la mesa estadounidense).
fuente
Respuestas:
El uso de un lenguaje de script en lugar de un gran archivo de configuración parece a primera vista: usted tiene todo el poder de ese idioma disponible y sólo se puede
eval()
oimport
ella. En la práctica, hay algunas trampas:Es un lenguaje de programación que necesita ser aprendido. Para editar la configuración, debe conocer este idioma lo suficientemente bien. Los archivos de configuración suelen tener un formato más simple que es más difícil de equivocar.
Es un lenguaje de programación, lo que significa que la configuración puede ser difícil de depurar. Con un archivo de configuración normal, lo mira y ve qué valores se proporcionan para cada propiedad. Con un script, posiblemente necesite ejecutarlo primero para ver los valores.
Es un lenguaje de programación, lo que hace difícil mantener una separación clara entre la configuración y el programa real. A veces quieres este tipo de extensibilidad, pero en ese momento probablemente estés buscando un sistema de plugins real.
es un lenguaje de programación, lo que significa que la configuración puede hacer cualquier cosa que el lenguaje de programación pueda hacer. Entonces, o está utilizando una solución de sandbox que niega gran parte de la flexibilidad del lenguaje, o está depositando una gran confianza en el autor de la configuración.
Por lo tanto, es probable que usar un script para la configuración esté bien si la audiencia de su herramienta son desarrolladores, por ejemplo, Sphinx config o setup.py en proyectos de Python. Otros programas con configuración ejecutable son shells como Bash y editores como Vim.
Es necesario usar un lenguaje de programación para la configuración si la configuración contiene muchas secciones condicionales, o si proporciona devoluciones de llamada / complementos. Usar un script directamente en lugar de eval (): algunos campos de configuración tienden a ser más depurables (¡piense en los seguimientos de pila y los números de línea!).
El uso directo de un lenguaje de programación también puede ser una buena idea si su configuración es tan repetitiva que está escribiendo scripts para autogenerar la configuración. ¿Pero quizás un mejor modelo de datos para la configuración podría eliminar la necesidad de una configuración tan explícita? Por ejemplo, puede ser útil si el archivo de configuración puede contener marcadores de posición que luego expandirá. Otra característica que se ve a veces es que varios archivos de configuración tienen una precedencia diferente que pueden anularse entre sí, aunque eso presenta algunos problemas propios.
En la mayoría de los casos, los archivos INI, los archivos de propiedades Java o los documentos YAML son mucho más adecuados para la configuración. Para modelos de datos complejos, XML también puede ser aplicable. Como ha notado, JSON tiene algunos aspectos que lo hacen inadecuado como un archivo de configuración editable por humanos, aunque es un excelente formato de intercambio de datos.
fuente
sendmail.cf
. Eso indicaría que usar un lenguaje de script real podría ser beneficioso, ya que ese fue diseñado para ser Turing completo. Sin embargo , Turing-completo y "Tetris-completitud" son dos cosas diferentes, y si biensendmail.cf
puede calcular funciones arbitrarias, que pueden no enviar su/etc/passwd
sobre la red o formatear el disco duro, lo que Python o Perl sería capaz de hacerlo.+1 a todo en la respuesta de amon . Me gustaría agregar esto:
Lamentará usar el código Python como su idioma de configuración la primera vez que quiera importar la misma configuración desde dentro del código escrito en un idioma diferente. Por ejemplo, si el código que forma parte de su proyecto y está escrito en C ++ o Ruby o algo más necesita incorporar la configuración, deberá vincular el intérprete de Python como una biblioteca o analizar la configuración en un coproceso de Python, ambos que son incómodos, difíciles o de alto costo.
Todo el código que importa esta configuración hoy puede estar escrito en Python, y puede pensar que esto también será cierto mañana, pero ¿está seguro?
Dijiste que usarías lógica (cualquier otra cosa que no sean estructuras de datos estáticos) en tu configuración con moderación, si es que es bueno, pero si hay algo de eso, te resultará difícil deshacerlo en el futuro para que puedas puede volver a un archivo de configuración declarativo.
EDITAR para el registro: varias personas han comentado sobre esta respuesta acerca de cuán probable o improbable es que un proyecto se reescriba completamente en otro idioma. Es justo decir que una reescritura completa compatible con versiones anteriores probablemente rara vez se ve. Lo que realmente tenía en mente era que partes del mismo proyecto (y que necesitaban acceso a la misma configuración) se escribieran en diferentes idiomas. Por ejemplo, servir pila en C ++ para velocidad, limpieza de bases de datos por lotes en Python, algunos scripts de shell como pegamento. Así que también piense en ese caso :)
fuente
key=value
asignaciones para la configuración, no veo por qué un programa Java / C ++ no puede leer el archivo Python como un archivo de texto sin formato y analizarlo de la misma manera si necesitan pasar a otra cosa en el futuro. No veo la necesidad de un intérprete completo de Python.Las otras respuestas ya son muy buenas, solo aportaré mi experiencia del uso en el mundo real en algunos proyectos.
Pros
En su mayoría ya están detallados:
eval
); funciona automáticamente incluso para tipos de datos más complejos (en nuestro programa, tenemos puntos geométricos y transformaciones, que se descargan / cargan perfectamente a través derepr
/eval
);Contras
repr
. Obviamente esto es algo malo.Incluso si está en Python, modificar el archivo de configuración desde el código es un problema real, porque ... bueno, modificar el código no es trivial en absoluto, especialmente el código que tiene una sintaxis rica y no está en LISP o similar. Uno de nuestros programas tiene un archivo de configuración que es Python, originalmente escrito a mano, pero que luego resultó ser útil para manipular a través del software (una configuración particular es una lista de cosas que es mucho más fácil reordenar usando una GUI). Este es un gran problema, porque:
Compare esto con JSON, INI o (¡Dios no lo quiera!) XML, donde la representación en memoria siempre se puede editar y volver a escribir sin pérdida de datos (XML, donde la mayoría de los analizadores DOM pueden mantener espacios en blanco en nodos de texto y nodos de comentarios) o al menos perder solo un poco de formato (JSON, donde el formato en sí mismo no permite mucho más que los datos sin procesar que está leyendo).
Entonces, como siempre, no hay una solución clara; mi política actual sobre el tema es:
si el archivo de configuración es:
un archivo Python puede ser una idea válida;
si en cambio:
un formato de "solo datos" puede ser una mejor idea.
Tenga en cuenta que no es necesario hacer una elección única: recientemente escribí una aplicación que usa ambos enfoques. Tengo un archivo casi nunca modificado con configuraciones manuscritas de primera configuración donde hay ventajas de tener bonificaciones bonificadas de Python y un archivo JSON para la configuración editada desde la interfaz de usuario.
fuente
note:
campo que se ignora para la configuración.curl ... | bash
, es aún menos molesto. :-PLa pregunta principal es: ¿desea que su archivo de configuración esté en algún lenguaje completo de Turing (como Python)? Si lo desea, también puede considerar incrustar algún otro lenguaje de script (Turing completo) como Guile o Lua (porque podría percibirse como "más simple" usar o incrustar que Python; lea el capítulo sobre Extensión y Incrustación de Python ). No lo discutiré más a fondo (porque otras respuestas, por ejemplo, Amon , lo discutieron en profundidad), pero tenga en cuenta que incrustar un lenguaje de secuencias de comandos en su aplicación es una elección arquitectónica importante , que debe considerar muy pronto; ¡Realmente no recomiendo hacer esa elección más tarde!
Un ejemplo bien conocido de un programa configurable a través de "scripts" es el editor GNU emacs (o probablemente AutoCAD en el dominio propietario); así que tenga en cuenta que si acepta las secuencias de comandos, algún usuario eventualmente usaría, y quizás abusaría, en su punto de vista, esa facilidad ampliamente y crearía una secuencia de comandos de miles de líneas; por lo tanto, la elección de un lenguaje de script suficientemente bueno es importante.
Sin embargo (al menos en los sistemas POSIX), usted podría considerar conveniente para que el "archivo" de configuración para ser dinámicamente calculada en tiempo de inicialización (por supuesto, dejando la carga de una configuración de cuerdo a su administrador del sistema o usuario, en realidad se trata de una configuración texto que proviene de algún archivo o de algún comando). Para eso, simplemente podría adoptar la convención (y documentarla ) de que una ruta de archivo de configuración que comienza con, por ejemplo,
!
ao a|
es en realidad un comando de shell que leería como una tubería . Esto deja a su usuario la opción de usar cualquier "preprocesador" o "lenguaje de script" con el que esté más familiarizado.(debe confiar en su usuario sobre los problemas de seguridad si acepta una configuración calculada dinámicamente)
Entonces, en su código de inicialización, su
main
(por ejemplo) aceptaría algún--config
argumentoconfarg
y obtendría algoFILE*configf;
de él. Si ese argumento comienza con!
(es decir, si(confarg[0]=='!')
...), usaríaconfigf = popen(confarg+1, "r");
y cerraría esa tubería conpclose(configf);
. De lo contrario, usaríaconfigf=fopen(confarg, "r");
y cerraría ese archivo confclose(configf);
(no olvide la comprobación de errores). Ver tubo (7) , popen (3) , fopen (3) . Para una aplicación codificada en Python, lea sobre os.popen , etc.(documento también para el usuario extraño que desea pasar un archivo de configuración llamado
!foo.config
a pasar./!foo.config
para evitar elpopen
truco anterior)Por cierto, tal truco es solo una conveniencia (para evitar que el usuario avanzado, por ejemplo, codifique algún script de shell para generar un archivo de configuración ). Si el usuario desea informar cualquier error, debe enviarle el archivo de configuración generado ...
Tenga en cuenta que también puede diseñar su aplicación con la capacidad de usar y cargar complementos en el momento de la inicialización, por ejemplo, con dlopen (3) (y debe confiar en su usuario acerca de ese complemento). Nuevamente, esta es una decisión arquitectónica muy importante (y debe definir y proporcionar una API y una convención bastante estables sobre estos complementos y su aplicación).
Para una aplicación codificada en un lenguaje de script como Python, también podría aceptar algún argumento de programa para eval o exec o primitivas similares. Una vez más, los problemas de seguridad son la preocupación del usuario (avanzado) .
Con respecto al formato de texto para su archivo de configuración (ya sea generado o no), creo que en su mayoría necesita documentarlo bien (y la elección de algún formato en particular no es tan importante; sin embargo, le recomiendo permitir que su usuario pueda poner algunos comentarios omitidos en su interior). Puede usar JSON (preferiblemente con algún analizador JSON que acepte y omita comentarios con
//
eol habitual o/*
...*/
...), YAML, XML o INI o lo suyo. Analizar un archivo de configuración es razonablemente fácil (y encontrará muchas bibliotecas relacionadas con esa tarea).fuente
Además de la respuesta de amon , ¿ha considerado alternativas? JSON es quizás más de lo que necesita, pero los archivos de Python probablemente le darán problemas en el futuro por las razones mencionadas anteriormente.
Sin embargo, Python ya tiene un analizador de configuración para un lenguaje de configuración muy simple que puede satisfacer todas sus necesidades. El
ConfigParser
módulo implementa un lenguaje de configuración simple.fuente
He trabajado durante mucho tiempo con un software conocido que tiene sus archivos de configuración escritos en TCL, por lo que la idea no es nueva. Esto funcionó bastante bien, ya que los usuarios que no conocían el idioma aún podían escribir / editar archivos de configuración simples usando una sola
set name value
declaración, mientras que los usuarios y desarrolladores más avanzados podrían hacer trucos sofisticados con esto.No creo que "los archivos de configuración puedan ser difíciles de depurar" es una preocupación válida. Siempre que su aplicación no obligue a los usuarios a escribir scripts, sus usuarios siempre pueden usar asignaciones simples en sus archivos de configuración, lo que difícilmente es más difícil de hacer en comparación con JSON o XML.
Reescribir la configuración es un problema, aunque no es tan malo como parece. Es imposible actualizar el código arbitrario, pero cargar la configuración desde un archivo, alterarlo y guardarlo nuevamente. Básicamente, si realiza algunas secuencias de comandos en un archivo de configuración que no es de solo lectura, terminará con una lista equivalente de
set name value
declaraciones una vez que se guarde. Una buena pista de que esto sucederá es un comentario de "no editar" al comienzo del archivo.Una cosa a tener en cuenta es que sus archivos de configuración no serán legibles de manera confiable mediante herramientas simples basadas en expresiones regulares, como
sed
, por lo que entiendo, este ya no es el caso con sus archivos JSON actuales, por lo que no hay mucho que perder.Solo asegúrese de utilizar las técnicas de sandboxing adecuadas al ejecutar sus archivos de configuración.
fuente
Además de todos los puntos válidos de otras buenas respuestas aquí (wow, incluso mencionaron el concepto completo de Turing), en realidad hay un par de razones prácticas sólidas para NO usar un archivo Python como su configuración, incluso cuando está trabajando en un Python- solo proyecto.
La configuración dentro de un archivo fuente de Python es técnicamente parte del código fuente ejecutable, en lugar de un archivo de datos de solo lectura. Si sigue esta ruta, normalmente lo haría
import config
, porque ese tipo de "conveniencia" fue presumiblemente una de las principales razones por las que las personas comenzaron a usar un archivo Python como configuración en primer lugar. Ahora tiende a asignar ese config.py en su repositorio, de lo contrario, su usuario final se encontrará con un ImportError confuso cuando intente ejecutar su programa por primera vez.Asumiendo que realmente está comprometiendo ese config.py en su repositorio, ahora los miembros de su equipo probablemente tendrían diferentes configuraciones en diferentes entornos. Imagine que algún día, de algún modo, algún miembro ingresa accidentalmente su archivo de configuración local en el repositorio.
Por último, pero no menos importante, su proyecto podría tener contraseñas en el archivo de configuración. (Esta es una práctica discutible en sí misma, pero ocurre de todos modos). Y si su archivo de configuración existe en el repositorio, corre el riesgo de comprometer su credencial en un repositorio público.
Ahora, el uso de un archivo de configuración de solo datos, como el formato JSON universal, puede evitar los 3 problemas anteriores, porque razonablemente puede pedirle al usuario que presente su propio config.json y lo incorpore a su programa.
PD: Es cierto que JSON tiene muchas restricciones. 2 de las limitaciones mencionadas por el OP, se pueden resolver con un poco de creatividad.
Y generalmente tengo un marcador de posición para omitir la regla de coma final. Me gusta esto:
fuente