Módulo ConfigParser
Introdução ao ConfigParserO módulo ConfigParser é utilizado para ler arquivos de configuração. A estrutura desses arquivos segue um formato específico, onde seções são definidas entre colchetes "[ ]", e cada seção contém pares chave-valor.
[banco_dados]
servidor = 127.0.0.1
porta = 22
usuario = root
senha = rootroot
[concorrencia]
threads = 10
processadores = 20
Os colchetes "[ ]" indicam seções, e logo após cada seção, temos opções no formato chave-valor.
Inicialização do ConfigParserPara utilizar o ConfigParser, primeiro precisamos criar uma instância e ler o arquivo de configuração:
configurador = ConfigParser.ConfigParser()
configurador.read("nome_do_arquivo.conf")
Métodos Comuns do ConfigParser****1. Obter todas as seções:
secoes = configurador.sections()
print('secoes:', secoes)
Saída (considerando o arquivo de exemplo):
secoes: ['banco_dados', 'concorrencia']
2. Obter opções de uma seção específica:
opcoes = configurador.options("banco_dados")
print('opcoes:', opcoes)
Saída:
opcoes: ['servidor', 'porta', 'usuario', 'senha']
3. Obter informações de configuração de uma seção:
valores = configurador.items("banco_dados")
print('banco_dados:', valores)
Saída:
banco_dados: [('servidor', '127.0.0.1'), ('porta', '22'), ('usuario', 'root'), ('senha', 'rootroot')]
4. Ler opções de uma seção com tipos específicos:
servidor = configurador.get("banco_dados", "servidor")
porta = configurador.getint("banco_dados", "porta")
usuario = configurador.get("banco_dados", "usuario")
senha = configurador.get("banco_dados", "senha")
# Retorna valores inteiros
threads = configurador.getint("concorrencia", "threads")
processadores = configurador.getint("concorrencia", "processadores")
print "servidor:", servidor
print "porta:", porta
print "usuario:", usuario
print "senha:", senha
print "threads:", threads
print "processadores:", processadores
Saída:
servidor: 127.0.0.1
porta: 22
usuario: root
senha: rootroot
threads: 10
processadores: 20
5. Definir o valor de uma opção (lembre-se de gravar as alterações):
configurador.set("banco_dados", "senha", "novasenha")
configurador.write(open("configuracao.conf", "w"))
6. Adicionar uma nova seção (também é necessário gravar):
configurador.add_section('nova_secao') configurador.set('nova_secao', 'numero_inteiro', '15') configurador.set('nova_secao', 'booleano', 'true') configurador.set('nova_secao', 'decimal', '3.1415') configurador.set('nova_secao', 'campo1', 'interessante') configurador.set('nova_secao', 'campo2', 'Python') configurador.set('nova_secao', 'campo3', '%(campo2)s é %(campo1)s!') configurador.write(open("configuracao.conf", "w"))
7. Remover seções ou opções (qualquer modificação deve ser gravada):
configurador.remove_option('nova_secao','numero_inteiro')
configurador.remove_section('nova_secao')
configurador.write(open("configuracao.conf", "w"))
No entanto, se o arquivo de configuração contiver parâmetros com colchetes "[ ]", este módulo não conseguirá解析. Portanto, ao usar Python como linguaegm de desenvolvimento, o formato do arquivo de configuração deve ser compatível com o formato esperado pelo módulo configparser.
O seguinte formato não pode ser processado pelo módulo ConfigParser, pois os colchetes serão interpretados como seções:
[banco_dados]
host[0]=127.0.0.1
host[1] = 192.168.1.123
porta=3306
usuario=root
senha=senha
[concorrencia]
threads=10
processadores=20
Problemas ao usar o módulo ConfigParser:
Os nomes dos parâmetros em maiúsculas são convertidos para minúsculas. Os nomes dos parâmetros não podem conter colchetes "[ ]". Se houver várias seções com o mesmo nome, a última seção será considerada. Outro problema é que se diferentes seções contiverem parâmetros com nomes idênticos, o módulo não conseguirá identificar de qual seção cada parâmetro pertence. Por exemplo, em arquivos de configuração do MySQL, tanto a seção "client" quanto a seção "mysqld" podem conter os parâmetros "socket" e "port". Com o módulo ConfigParser, não é possível determinar a qual seção esses parâmetros pertencem.
Módulo ElementTree
Processando XML em Python com ElementTreeQuando é necessário processar e manipular arquivos XML, Python demonstra sua filosofia "baterias inclusas". A vasta gama de módulos e ferramentas disponíveis na biblioteca padrão é suficiente tanto para iniciantes em Python quanto para especialistas em XML.
O módulo ElementTree oferece uma API leve e intuitiva para trabalhar com documentos XML, representando-os como árvores hierárquicas, o que é uma abordagem natural para o formato XML.
Carregando e analisando XMLVamos começar com o básico. XML é um formato de dados hierárquico, e a representação mais natural é como uma árvore. O ElementTree possui dois objetos principais para esse propósito: ElementTree, que representa todo o documento XML como uma árvore, e Element, que representa um único nó na árvore.
Para operações no nível do documento (como leitura, escrita ou encontrar elementos interessantes), geralmente usamos ElementTree. Para elementos XML individuais e seus filhos, usamos Element.
Considere o seguinte arquivo XML como exemplo:
<?xml version="1.0"?>
<documento>
<ramo nome="teste" hash="1cdf045c">
texto,fonte
</ramo>
<ramo nome="versao01" hash="f200013e">
<sub-ramo nome="subversao01">
xml,sgml
</sub-ramo>
</ramo>
<ramo nome="invalido">
</ramo>
</documento>
Carregando e analisando este XML:
>>> import xml.etree.cElementTree as ET
>>> arvore = ET.ElementTree(file='documento.xml')
Obtendo o elemento raiz:
>>> arvore.getroot()
<Element 'documento' at 0x11eb780>
Como esperado, a raiz é um elemento Element. Podemos inspecioná-lo:
>>> raiz = arvore.getroot()
>>> raiz.tag, raiz.attrib
('documento', {})
Como podemos ver, o elemento raiz não tem atributos. Assim como qualquer Element, ele pode encontrar seus elementos filhos:
>>> for_filho_da_raiz in raiz:
... print for_filho_da_raiz.tag, for_filho_da_raiz.attrib
...
ramo {'hash': '1cdf045c', 'nome': 'teste'}
ramo {'hash': 'f200013e', 'nome': 'versao01'}
ramo {'nome': 'invalido'}
Também podemos acessar um elemento filho específico:
>>> raiz[0].tag, raiz[0].text
('ramo', '\n texto,fonte\n ')
Encontrando elementos de interesseÉ fácil ver que podemos usar uma recursão simples para obter qualquer elemento em um XML. No entanto, como essa operação é comum, o ET fornece ferramentas úteis para simplificar o processo.
O objeto Element tem um método iter que percorre os nós filhos em profundidade. O objeto ElementTree também tem um método iter para conveniência.
>>> for elem in arvore.iter():
... print elem.tag, elem.attrib
...
documento {}
ramo {'hash': '1cdf045c', 'nome': 'teste'}
ramo {'hash': 'f200013e', 'nome': 'versao01'}
sub-ramo {'nome': 'subversao01'}
ramo {'nome': 'invalido'}
Percorre todos os elementos e verifica se há algum de interesse. O ET torna esse processo mais conveniente. O método iter aceita um nome de tag e percorre apenas elementos com essa tag específica:
>>> for elem in arvore.iter(tag='ramo'):
... print elem.tag, elem.attrib
...
ramo {'hash': '1cdf045c', 'nome': 'teste'}
ramo {'hash': 'f200013e', 'nome': 'versao01'}
ramo {'nome': 'invalido'}
Para encontrar elementos de interesse de forma mais eficiente, podemos usar o suporte a XPath. O Element tem vários métodos de busca que podem aceitar XPath como parâmetro. O find retorna o primeiro elemento filho correspondente, o findall retorna todos os elementos correspondentes como uma lista, e o iterfornece um iterador para todos os correspondentes. Esses métodos também estão disponíveis em ElementTree.
>>> for elem in arvore.iterfind('ramo/sub-ramo'):
... print elem.tag, elem.attrib
...
sub-ramo {'nome': 'subversao01'}
Este exemplo encontra todos os elementos com a tag sub-ramo dentro de elementos ramo. Agora, vamos encontrar todos os elementos ramo com um atributo nome específico:
>>> for elem in arvore.iterfind('ramo[@nome="versao01"]'):
... print elem.tag, elem.attrib
...
ramo {'hash': 'f200013e', 'nome': 'versao01'}
Criando documentos XMLO ET fornece maneiras convenientes de criar documentos XML e gravá-los em arquivos. O objeto ElementTree tem um método write.
Aqui estão dois scripts comuns para gravar documentos XML. Para modificar um documento, podemos usar os métodos do objeto Element:
>>> raiz = arvore.getroot()
>>> del raiz[2]
>>> raiz[0].set('novo_atributo', 'valor')
>>> for subelem in raiz:
... print subelem.tag, subelem.attrib
...
ramo {'novo_atributo': 'valor', 'hash': '1cdf045c', 'nome': 'teste'}
ramo {'hash': 'f200013e', 'nome': 'versao01'}
Neste exemplo, excluímos o terceiro elemento filho da raiz e adicionamos um novo atributo ao primeiro elemento filho. Em seguida, a árvore pode ser gravada de volta em um arquivo.
>>> import sys
>>> arvore.write(sys.stdout)
<documento>
<ramo novo_atributo="valor" hash="1cdf045c" nome="teste">
texto,fonte
</ramo>
<ramo hash="f200013e" nome="versao01">
<sub-ramo nome="subversao01">
xml,sgml
</sub-ramo>
</ramo>
</documento>
Note que a ordem dos atributos não é a mesma do documento original. Isso ocorre porque o ET armazena atributos em um dicionário não ordenado. Semanticamente, a ordem dos atributos em XML não é significativa.
Criar um elemento completamente novo também é fácil. O módulo ET fornece a função SubElement para simplificar o processo:
>>> a = ET.Element('elemento')
>>> c = ET.SubElement(a, 'filho1')
>>> c.text = "algum texto"
>>> d = ET.SubElement(a, 'filho2')
>>> b = ET.Element('elemento_b')
>>> raiz = ET.Element('raiz')
>>> raiz.extend((a, b))
>>> arvore = ET.ElementTree(raiz)
>>> arvore.write(sys.stdout)
<raiz><elemento><filho1>algum texto</filho1><filho2 /></elemento><elemento_b /></raiz>
ConclusãoEntre os muitos módulos de processamento de XML em Python, o ElementTree se destaca por combinar uma API leve e intuitiva com excelente desempenho, alinhada com a filosofia Python. Para qualquer tarefa de processamento de XML, ele é a escolha ideal!
Módulo shutil
**Parâmetros principais:**shutil.copyfile(origem, destino) - Copia do arquivo de origem para o destino. O destino deve ter permissão de escrita. Lança exceção IOError se o destino não existir ou não puder ser escrito. Se o destino já existir, será sobrescrito. shutil.move(origem, destino) - Move ou renomeia um arquivo ou diretório. shutil.copymode(origem, destino) - Copia apenas as permissões do arquivo, mantendo o resto inalterado. shutil.copystat(origem, destino) - Copia permissões, tempo de acesso e tempo de modificação. shutil.copy(origem, destino) - Copia um arquivo para outro arquivo ou diretório. shutil.copy2(origem, destino) - Similar ao copy, mas também copia os timestamps de acesso e modificação. shutil.copytree(diretorio_antigo, diretorio_novo, True/False) - Copia todo o conteúdo de diretorio_antigo para diretorio_novo. Se o terceiro parâmetro for True, mantém os links simbólicos; se for False, cria cópias físicas. shutil.rmtree(origem) - Exclui recursivamente um diretório e todo o seu conteúdo.
Módulo subprocess
A partir do Python 2.4, o módulo subprocess foi introduzido para gerenciar subprocessos, substituindo métodos de módulos mais antigos como os.system, os.spawn*, os.popen*, popen2.* e commands.*. Ele não só permite chamar comandos externos como subprocessos, mas também permite conectar-se às entradas, saídas e erros dos subprocessos para obter informações de retorno.
Funções封装 do subprocessO módulo subprocess define várias funções para criar subprocessos, cada uma com uma abordagem diferente. Além disso, ele fornece ferramentas para gerenciar streams padrão e pipes.
**subprocess.call()**O processo pai espera o subprocesso ser concluído. Retorna o código de saída (returncode, equivalente ao exit code do Linux).
**subprocess.check_call()**O processo pai espera o subprocesso ser concluído. Retorna 0 em caso de sucesso. Verifica o código de saída; se não for 0, lança um erro subprocess.CalledProcessError, que contém o atributo returncode.
**subprocess.check_output()**O processo pai espera o subprocesso ser concluído. Retorna a saída padrão do subprocesso. Verifica o código de saída; se não for 0, lança um erro subprocess.CalledProcessError, que contém os atributos returncoed e output.
Essas funções têm uso semelhante. Vamos usar subprocess.call() como exemplo:
>>> import subprocess
>>> codigo_retorno = subprocess.call(["ls", "-l"])
# Exibe o mesmo resultado que o comando ls -a no shell
>>> print codigo_retorno
0
O nome do programa (ls) e seus parâmetros (-l) são passados juntos em uma lista para subprocess.call(). O shell padrão é False. No Linux, quando shell=False, o Popen chama os.execvp() para executar o programa especificado em args; quando shell=True, se args for uma string, o Popen chama diretamente o shell do sistema para executar args; se args for uma sequência, o primeiro item é o comando do programa e os demais são parâmetros adicionais para o shell.
O exemplo acima também pode ser escrito como:
>>> codigo_retorno = subprocess.call("ls -l", shell=True)
subprocess.Popen()
class Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
Na verdade, as funções mencionadas acima são封装 baseadas em Popen(). O objetivo dessas funções é facilitar o uso de subprocessos. Quando precisamos de um controle mais personalizado, recorremos à classe Popen, cujos objetos representam subprocessos.
Diferentemente dos封装 mencionados, quando um objeto Popen é criado, o programa principal não aguarda automaticamente a conclusão do subprocesso. Devemos chamar o método wait() do objeto para que o processo pai aguarde (bloqueie). Por exemplo:
>>> import subprocess
>>> filho = subprocess.Popen(['ping','-c','4','blog.linuxeye.com'])
>>> print 'processo pai'
Como podemos ver, o processo pai, após iniciar o subprocesso, não espera sua conclusão e executa o print diretamente.
Compare com a situação de espera:
>>> import subprocess
>>> filho = subprocess.Popen('ping -c4 blog.linuxeye.com', shell=True)
>>> filho.wait()
>>> print 'processo pai'
Neste caso, o processo pai espera a conclusão do subprocesso antes de executar o print.
Além disso, podemos realizar outras operações no subprocesso, como no objeto filho do exemplo anterior:
filho.poll() # Verifica o status do subprocesso
filho.kill() # Termina o subprocesso
filho.send_signal() # Envia um sinal para o subprocesso
filho.terminate() # Termina o subprocesso
O PID do subprocesso é armazenado em filho.pid.
Controle de streams de texto do subprocessoAs entradas padrão, saídas padrão e erros padrão do subprocesso são representadas pelos seguintes atributos:
filho.stdin
filho.stdout
filho.stderr
Ao criar um subprocesso com Popen(), podemos alterar as entradas padrão, saídas padrão e erros padrão, e podemos usar subprocess.PIPE para conectar as entradas e saídos de múltiplos subprocessos, formando pipes. Dois exemplos:
>>> import subprocess
>>> filho1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
>>> print filho1.stdout.read(),
Ou usando filho1.communicate()
>>> import subprocess
>>> filho1 = subprocess.Popen(["cat","/etc/passwd"], stdout=subprocess.PIPE)
>>> filho2 = subprocess.Popen(["grep","0:0"], stdin=filho1.stdout, stdout=subprocess.PIPE)
>>> saida = filho2.communicate()
subprocess.PIPE na verdade fornece um buffer para streams de texto. A stdout do filho1 envia texto para o buffer, e a stdin do filho2 lê o texto a partir desse buffer. A saída do filho2 também é armazenada no PIPE até que o método communicate() leia o texto do PIPE.
Nota: communicate() é um método do objeto Popen que bloqueia o processo pai até que o subprocesso seja concluído.