Eu sempre usei arquivos JSON para configuração dos meus aplicativos. Comecei a usá-los quando codifiquei muito Java e agora estou trabalhando principalmente no desenvolvimento de Python no lado do servidor e na ciência de dados e não tenho certeza se o JSON é o caminho certo a seguir.
Eu vi o Celery usar arquivos Python reais para configuração. Inicialmente fiquei cético sobre isso. Mas a ideia de usar estruturas de dados Python simples para configuração está começando a crescer em mim. Alguns profissionais:
- As estruturas de dados serão as mesmas que eu normalmente codifico. Portanto, não preciso mudar de ideia.
- Meu IDE (PyCharm) entende a conexão entre configuração e código. Ctrl+ Btorna possível alternar facilmente entre configuração e código.
- Não preciso trabalhar com o JSON estrito desnecessário da IMO . Estou olhando para você aspas duplas, sem vírgulas e sem comentários.
- Posso escrever configurações de teste no aplicativo em que estou trabalhando e depois transportá-las facilmente para um arquivo de configuração sem precisar fazer nenhuma conversão e análise JSON.
- É possível criar scripts muito simples no arquivo de configuração, se realmente necessário. (Embora isso deva ser muito, muito limitado.)
Então, minha pergunta é: se eu trocar, como vou me dar um tiro no pé?
Nenhum usuário final não qualificado usará os arquivos de configuração. No momento, quaisquer alterações nos arquivos de configuração são confirmadas no Git e lançadas em nossos servidores como parte da implantação contínua. Não há alterações manuais na configuração, a menos que haja uma emergência ou ela esteja em desenvolvimento.
(Eu considerei o YAML , mas algo me irrita. Então, por enquanto, está fora da mesa americana.)
fonte
Respostas:
Usando uma linguagem de script no lugar de um arquivo de configuração parece ótimo à primeira vista: você tem todo o poder de que a linguagem disponível e pode simplesmente
eval()
ouimport
ele. Na prática, existem algumas dicas:é uma linguagem de programação que precisa ser aprendida. Para editar a configuração, você precisa conhecer esse idioma suficientemente bem. Os arquivos de configuração geralmente têm um formato mais simples, mais difícil de errar.
é uma linguagem de programação, o que significa que a configuração pode ficar difícil de depurar. Com um arquivo de configuração normal, você olha para ele e vê quais valores são fornecidos para cada propriedade. Com um script, você potencialmente precisará executá-lo primeiro para ver os valores.
é uma linguagem de programação, o que dificulta a manutenção de uma separação clara entre a configuração e o programa real. Às vezes, você deseja esse tipo de extensibilidade, mas nesse ponto provavelmente está procurando um sistema de plug-ins real.
é uma linguagem de programação, o que significa que a configuração pode fazer qualquer coisa que a linguagem de programação possa fazer. Então, você está usando uma solução sandbox que nega grande parte da flexibilidade do idioma ou está confiando muito no autor da configuração.
Portanto, é provável que o uso de um script para configuração seja bom se o público da sua ferramenta for desenvolvedor, por exemplo, Sphinx config ou o setup.py em projetos Python. Outros programas com configuração executável são shells como o Bash e editores como o Vim.
O uso de uma linguagem de programação para configuração é necessário se a configuração contiver muitas seções condicionais ou se fornecer retornos de chamada / plug-ins. Usar um script diretamente em vez de eval () - algum campo de configuração tende a ser mais debugável (pense nos rastreamentos da pilha e nos números de linha!).
Usar diretamente uma linguagem de programação também pode ser uma boa idéia se sua configuração for tão repetitiva que você estiver escrevendo scripts para gerar automaticamente a configuração. Mas talvez um modelo de dados melhor para a configuração possa remover a necessidade de tal configuração explícita? Por exemplo, pode ser útil se o arquivo de configuração puder conter espaços reservados que você expandir posteriormente. Outro recurso visto às vezes são vários arquivos de configuração com precedência diferente que podem se sobrepor, embora isso introduza alguns problemas.
Na maioria dos casos, arquivos INI, arquivos de propriedades Java ou documentos YAML são muito mais adequados para configuração. Para modelos de dados complexos, o XML também pode ser aplicável. Como você observou, o JSON tem alguns aspectos que o tornam inadequado como um arquivo de configuração editável por humanos, embora seja um bom formato de troca de dados.
fonte
sendmail.cf
. Isso indicaria que o uso de uma linguagem de script real pode ser benéfica, pois essa foi realmente projetada para ser completa em Turing. No entanto , Turing-completeness e "Tetris-completeness" são duas coisas diferentes e, emborasendmail.cf
possam computar funções arbitrárias, ele não pode enviar sua/etc/passwd
através da rede ou formatar seu disco rígido, o que Python ou Perl seriam capazes.+1 em tudo na resposta de amon . Eu gostaria de adicionar isto:
Você se arrependerá de usar o código Python como idioma de configuração na primeira vez que desejar importar a mesma configuração de dentro do código escrito em um idioma diferente. Por exemplo, se o código que faz parte do seu projeto e escrito em C ++ ou Ruby ou algo mais precisar puxar a configuração, será necessário vincular o interpretador Python como uma biblioteca ou analisar a configuração em um coprocesso Python, ambos que são difíceis, difíceis ou de alto custo.
Todo o código que importa essa configuração hoje pode ser escrito em Python, e você pode pensar que isso também será verdade amanhã, mas você tem certeza?
Você disse que usaria lógica (qualquer outra coisa que não estruturas de dados estáticas) em sua configuração com moderação, o que é bom, mas se houver algo disso, será difícil no futuro desfazê-lo para que você pode voltar para um arquivo de configuração declarativo.
EDIT para o registro: várias pessoas comentaram sobre esta resposta sobre a probabilidade ou a probabilidade de um projeto ser completamente reescrito com sucesso em outro idioma. É justo dizer que uma reescrita completa compatível com versões anteriores raramente é vista. O que eu realmente tinha em mente eram partes do mesmo projeto (e que precisavam de acesso à mesma configuração) sendo escritas em diferentes idiomas. Por exemplo, servindo pilha em C ++ para velocidade, limpeza de banco de dados em lote em Python, alguns scripts de shell como cola. Então pense também nesse caso :)
fonte
key=value
atribuições para a configuração, não vejo por que um programa Java / C ++ não pôde ler o arquivo Python como um arquivo de texto sem formatação e analisá-lo da mesma forma, se precisar passar para outra coisa em o futuro. Não vejo necessidade de um intérprete de Python completo.As outras respostas já são muito boas, trarei minha experiência de uso no mundo real em alguns projetos.
Prós
Eles já estão detalhados:
eval
); funciona automaticamente mesmo para tipos de dados mais complexos (em nosso programa, temos pontos e transformações geométricas, que são despejadas / carregadas com precisãorepr
/eval
);Contras
repr
. Obviamente, isso é uma coisa ruim.Mesmo se você estiver no Python, modificar o arquivo de configuração do código é um problema real, porque ... bem, a modificação do código não é trivial, especialmente o código que possui uma sintaxe rica e não está no LISP ou similar. Um programa nosso tem um arquivo de configuração que é Python, originalmente escrito à mão, mas que mais tarde resultou que seria útil manipular via software (uma configuração específica é uma lista de coisas que são muito mais fáceis de reordenar usando uma GUI). Este é um grande problema, porque:
Compare isso com JSON, INI ou (Deus não permita!) XML, onde a representação na memória sempre pode ser editada e gravada sem perda de dados (XML, onde a maioria dos analisadores DOM pode manter espaço em branco nos nós de texto e nos comentários) ou pelo menos perdendo apenas alguma formatação (JSON, onde o formato em si não permite muito mais do que os dados brutos que você está lendo).
Portanto, como sempre, não há uma solução clara; minha política atual sobre o assunto é:
se o arquivo de configuração for:
um arquivo Python pode ser uma ideia válida;
se em vez disso:
um formato "apenas dados" pode ser uma ideia melhor.
Observe que não é necessário fazer uma única escolha - recentemente escrevi um aplicativo que usa as duas abordagens. Eu tenho um arquivo quase nunca modificado com configurações manuscritas de primeira instalação, nas quais há vantagens em ter ótimos bônus em Python e um arquivo JSON para configuração editado a partir da interface do usuário.
fonte
note:
campo que é ignorado para a configuração.curl ... | bash
, é ainda menos complicado. :-PA principal questão é: você deseja que seu arquivo de configuração esteja em uma linguagem completa de Turing (como é o Python)? Se você quiser isso, considere também incorporar alguma outra linguagem de script (completa de Turing) como Guile ou Lua (porque pode ser percebido como "mais simples" de usar ou incorporar do que o Python; leia o capítulo em Estendendo e Incorporando Python ). Não discutirei mais isso (porque outras respostas - por Amon - discutiram isso em profundidade), mas observe que a incorporação de uma linguagem de script em seu aplicativo é uma das principais opções de arquitetura , que você deve considerar muito cedo; Eu realmente não recomendo fazer essa escolha mais tarde!
Um exemplo bem conhecido de um programa configurável através de "scripts" é o editor GNU emacs (ou provavelmente o AutoCAD na região proprietária); lembre-se de que, se você aceitar scripts, algum usuário poderá eventualmente usar - e talvez abusar, no seu ponto de vista - esse recurso extensivamente e criar um script com milhares de linhas; portanto, a escolha de uma linguagem de script suficientemente boa é importante.
No entanto (pelo menos nos sistemas POSIX), você pode considerar conveniente permitir que o "arquivo" de configuração seja computado dinamicamente no momento da inicialização (é claro, deixando a carga de uma configuração sã para o administrador ou usuário do sistema; na verdade, é uma configuração texto que vem de algum arquivo ou de algum comando). Para isso, você pode simplesmente adotar a convenção (e documentá- la) de que um caminho de arquivo de configuração começando com, por exemplo, a
!
ou a|
é na verdade um comando shell que você leria como um pipeline . Isso deixa o usuário com a opção de usar qualquer "pré-processador" ou "linguagem de script" com a qual ele esteja mais familiarizado.(você precisa confiar no usuário sobre problemas de segurança se aceitar uma configuração dinamicamente calculada)
Portanto, no seu código de inicialização, você
main
(por exemplo) aceitaria algum--config
argumentoconfarg
e obteriaFILE*configf;
dele. Se esse argumento começar com!
(ou seja, se(confarg[0]=='!')
....), você usariaconfigf = popen(confarg+1, "r");
e fecharia esse pipe compclose(configf);
. Caso contrário, você usariaconfigf=fopen(confarg, "r");
e fecharia esse arquivofclose(configf);
(não se esqueça da verificação de erros). Veja tubo (7) , tampa (3) , tampa (3) . Para uma aplicação codificada em Python leia sobre os.popen , etc ...(documento também para o usuário estranho que deseja passar um arquivo de configuração nomeado
!foo.config
para passar./!foo.config
para ignorar opopen
truque acima)BTW, esse truque é apenas uma conveniência (para evitar exigir que o usuário avançado codifique, por exemplo, algum shell script para gerar um arquivo de configuração ). Se o usuário desejar relatar algum bug, ele deverá enviar o arquivo de configuração gerado ...
Observe que você também pode projetar seu aplicativo com a capacidade de usar e carregar plugins no momento da inicialização, por exemplo, com dlopen (3) (e você precisa confiar no usuário sobre esse plug-in). Novamente, essa é uma decisão arquitetural muito importante (e você precisa definir e fornecer alguma API e convenção bastante estável sobre esses plug-ins e seu aplicativo).
Para um aplicativo codificado em uma linguagem de script como Python, você também pode aceitar algum argumento de programa para eval ou exec ou primitivas similares. Novamente, os problemas de segurança são a preocupação do usuário (avançado) .
Em relação ao formato textual do seu arquivo de configuração (gerado ou não), acredito que você precisa documentá-lo bem (e a escolha de um formato específico não é tão importante; no entanto, recomendo que seu usuário possa colocar alguns comentários ignorados). Você pode usar JSON (de preferência com algum analisador JSON aceitando e pulando comentários com o uso normal
//
até eol ou/*
...*/
...), ou YAML, XML ou INI ou suas próprias coisas. A análise de um arquivo de configuração é razoavelmente fácil (e você encontrará muitas bibliotecas relacionadas a essa tarefa).fonte
Adicionando à resposta de amon , você considerou alternativas? Talvez o JSON seja mais do que você precisa, mas os arquivos Python provavelmente apresentarão problemas no futuro pelos motivos mencionados acima.
No entanto, o Python já possui um analisador de configuração para uma linguagem de configuração muito simples que pode atender a todas as suas necessidades. O
ConfigParser
módulo implementa uma linguagem de configuração simples.fonte
Eu trabalhei por um longo tempo com algum software conhecido que tem seus arquivos de configuração escritos em TCL, então a idéia não é nova. Isso funcionou muito bem, já que os usuários que não conheciam o idioma ainda podiam escrever / editar arquivos de configuração simples usando uma única
set name value
declaração, enquanto usuários e desenvolvedores mais avançados podiam usar truques sofisticados com isso.Eu não acho que "os arquivos de configuração podem ficar difíceis de depurar" é uma preocupação válida. Desde que seu aplicativo não force os usuários a escrever scripts, eles sempre poderão usar atribuições simples em seus arquivos de configuração, o que dificilmente será mais difícil de acertar em comparação com JSON ou XML.
Reescrever a configuração é um problema, embora não seja tão ruim quanto parece. Atualizar código arbitrário é impossível, mas carregar a configuração de um arquivo, alterá-lo e salvá-lo novamente. Basicamente, se você criar algum script em um arquivo de configuração que não seja somente leitura, você acabará com uma lista equivalente de
set name value
instruções assim que ela for salva. Uma boa dica de que isso acontecerá é um comentário "não edite" no início do arquivo.Uma coisa a considerar é que seus arquivos de configuração não serão legíveis de forma confiável por ferramentas simples baseadas em regex, como
sed
, mas até onde eu entendo, esse já não é o caso dos seus arquivos JSON atuais, portanto, não há muito a perder.Apenas certifique-se de usar técnicas apropriadas de sandbox ao executar seus arquivos de configuração.
fonte
Além de todos os pontos válidos de outras boas respostas aqui (uau, eles até mencionaram o conceito Turing-complete), na verdade existem algumas razões práticas sólidas para NÃO usar um arquivo Python como sua configuração, mesmo quando você estiver trabalhando em um Python- único projeto.
As configurações dentro de um arquivo de origem Python são tecnicamente parte do código-fonte executável, em vez de um arquivo de dados somente leitura. Se você seguir essa rota, normalmente faria isso
import config
, porque esse tipo de "conveniência" provavelmente foi um dos principais motivos pelos quais as pessoas começaram a usar um arquivo Python como configuração em primeiro lugar. Agora você tende a confirmar esse config.py em seu repositório, caso contrário, o usuário final encontrará um ImportError confuso ao tentar executar o programa pela primeira vez.Supondo que você realmente confirme esse config.py em seu repositório, agora os membros da sua equipe provavelmente terão configurações diferentes em ambientes diferentes. Imagine que algum dia algum membro comprometa acidentalmente seu arquivo de configuração local no repositório.
Por último, mas não menos importante, seu projeto pode ter senhas no arquivo de configuração. (Essa é uma prática discutível por si só, mas acontece de qualquer maneira.) E se o seu arquivo de configuração existir no repositório, você corre o risco de comprometer sua credencial em um repositório público.
Agora, o uso de um arquivo de configuração somente de dados, como o formato JSON universal, pode evitar todos os 3 problemas acima, porque você pode razoavelmente pedir ao usuário que crie seu próprio config.json e o alimente no seu programa.
PS: É verdade que o JSON tem muitas restrições. 2 das limitações mencionadas no OP podem ser resolvidas com alguma criatividade.
E geralmente tenho um espaço reservado para ignorar a regra de vírgula à direita. Como isso:
fonte