plistlib no Python: arquivos plist

Publicado em: 08/08/2026
Tempo de leitura: 7 minutos
Ícone de configuração representando arquivos plist com plistlib no Python

Aplicativos e ferramentas do ecossistema Apple usam arquivos property list para armazenar configurações, metadados e estruturas simples. O módulo plistlib no Python lê e grava plists XML e binários sem depender do macOS, permitindo automatizar builds, analisar preferências, gerar arquivos de configuração e interoperar com ferramentas de iOS e macOS.

O formato aceita dicionários, listas, strings, números, booleanos, bytes e datas, mas não é uma serialização arbitrária de objetos Python. Chaves devem ser strings e valores fora do conjunto suportado geram erros. Neste guia você aprenderá a escolher o formato, tratar datas, validar entradas, usar arquivos temporários e evitar riscos ao processar plists externos.

O conteúdo complementa nossos artigos sobre netrc, tempfile, filecmp, importlib.resources e copy.

O que é um property list

Um plist representa uma árvore de valores simples. O objeto raiz costuma ser um dicionário, mas listas e outros tipos compatíveis também podem aparecer. O formato é usado por preferências, manifests, metadados de aplicativos e arquivos produzidos por ferramentas Apple.

Existem duas variantes principais: XML, legível e fácil de comparar, e binária, mais compacta e rápida para algumas cargas.

Ler um arquivo plist

import plistlib

with open("config.plist", "rb") as arquivo:
    dados = plistlib.load(arquivo)

print(dados)

Abra o arquivo em modo binário. Quando fmt=None, que é o padrão, o módulo tenta detectar automaticamente se o conteúdo é XML ou binário.

Ler bytes em memória

conteudo = caminho.read_bytes()
dados = plistlib.loads(conteudo)

loads() é útil quando o conteúdo vem de uma resposta HTTP, um arquivo zip, um recurso empacotado ou um banco de dados. A entrada normalmente é bytes; no Python 3.13, strings também podem ser aceitas quando o formato XML é indicado.

Gravar um arquivo XML

config = {
    "Nome": "Meu Aplicativo",
    "Versao": 3,
    "Ativo": True,
    "Recursos": ["sincronizacao", "backup"],
}

with open("config.plist", "wb") as arquivo:
    plistlib.dump(config, arquivo, fmt=plistlib.FMT_XML)

XML é conveniente em repositórios, revisões de código e diagnósticos. Entretanto, arquivos grandes podem ocupar mais espaço do que a representação binária.

Gravar formato binário

with open("config-binary.plist", "wb") as arquivo:
    plistlib.dump(
        config,
        arquivo,
        fmt=plistlib.FMT_BINARY,
    )

O formato binário preserva os mesmos tipos básicos, mas não é apropriado para edição manual. Use ferramentas específicas para inspeção quando necessário.

Serializar para bytes

xml = plistlib.dumps(config, fmt=plistlib.FMT_XML)
binario = plistlib.dumps(config, fmt=plistlib.FMT_BINARY)

Essa API facilita testes, respostas de serviços e integração com bibliotecas que aceitam buffers em memória.

Tipos suportados

O módulo aceita strings, inteiros, floats, booleanos, listas, tuplas, dicionários com chaves string, bytes, bytearray e objetos datetime. Estruturas podem combinar esses valores recursivamente.

Objetos personalizados, sets, Decimal e caminhos não são convertidos automaticamente. Transforme-os em uma estrutura declarativa antes da serialização.

Chaves precisam ser strings

dados = {1: "valor"}
plistlib.dumps(dados)  # TypeError

Com skipkeys=True, chaves inválidas são ignoradas. Essa opção pode causar perda silenciosa de dados, portanto o padrão False é mais seguro.

Valide o modelo antes de escrever e converta chaves apenas quando a transformação estiver definida pelo contrato.

Ordenação de chaves

sort_keys=True é o padrão e produz dicionários em ordem de chave. Isso ajuda a criar XML estável e diffs menores.

plistlib.dumps(dados, sort_keys=False)

Desabilite a ordenação quando a ordem de inserção tiver valor para ferramentas humanas. Consumidores não devem depender da ordem sem uma especificação explícita.

Datas e timezone

Property lists representam datas em UTC. Desde Python 3.13, aware_datetime=True permite ler objetos com tzinfo=datetime.UTC e converter datas aware para UTC durante a gravação.

from datetime import datetime, UTC

config = {"GeradoEm": datetime.now(UTC)}
conteudo = plistlib.dumps(
    config,
    aware_datetime=True,
)

Evite datetimes ingênuos quando o valor representa um instante real. Defina uma política explícita e teste a conversão ao atravessar fusos horários.

Datas ao carregar

dados = plistlib.loads(
    conteudo,
    aware_datetime=True,
)
print(dados["GeradoEm"].tzinfo)

Sem essa opção, o comportamento tradicional produz datas sem timezone. Misturar os dois modos pode gerar comparações incorretas.

UIDs de plists binários

plistlib.UID representa identificadores usados por dados do NSKeyedArchiver.

uid = plistlib.UID(42)
print(uid.data)

O valor deve ficar entre zero e 2**64 - 1. Um UID não é uma referência resolvida automaticamente; interpretar um archive completo exige conhecer a estrutura do formato produtor.

Não trate NSKeyedArchiver como dicionário comum

Plists de NSKeyedArchiver armazenam tabelas de objetos e referências por UID. Ler o plist é apenas a primeira etapa. Não assuma que os campos representam diretamente o modelo final.

Ao analisar archives desconhecidos, limite tamanho, profundidade e quantidade de objetos.

Erros de arquivo inválido

Conteúdo que não pode ser analisado gera plistlib.InvalidFileException. XML malformado também pode provocar exceções do parser Expat.

try:
    dados = plistlib.loads(conteudo)
except (plistlib.InvalidFileException, ValueError) as erro:
    raise RuntimeError("plist inválido") from erro

Não mostre todo o conteúdo externo em mensagens de erro, pois ele pode conter dados sensíveis ou muito grandes.

Inteiros fora do intervalo

O formato binário possui limites para inteiros. Valores não representáveis geram OverflowError.

Valide intervalos na camada de modelo e não dependa apenas da exceção do serializer.

XML e segurança

O parser usa Expat para XML. Ainda assim, trate plists externos como entrada não confiável. Estabeleça limites de tamanho antes de carregar e não interprete strings internas como caminhos, comandos ou código.

Elementos desconhecidos podem ser ignorados pelo parser de plist, portanto uma leitura bem-sucedida não confirma conformidade com o esquema da sua aplicação.

Validar o modelo

def validar_config(dados):
    if not isinstance(dados, dict):
        raise TypeError("a raiz deve ser um dicionário")
    if not isinstance(dados.get("Nome"), str):
        raise ValueError("Nome ausente ou inválido")
    if dados.get("Versao", 0) < 1:
        raise ValueError("Versao inválida")

O módulo garante a sintaxe do plist, não as regras de negócio. Valide campos obrigatórios, tipos, limites, enumerações e relações.

Escrever de forma atômica

Não grave diretamente sobre um arquivo crítico. Crie um temporário no mesmo diretório, aplique permissões, serialize, faça flush e substitua o destino.

from pathlib import Path
import tempfile
import os

final = Path("config.plist")
with tempfile.NamedTemporaryFile(
    dir=final.parent,
    delete=False,
) as tmp:
    plistlib.dump(config, tmp)
    tmp.flush()
    os.fsync(tmp.fileno())
    temporario = Path(tmp.name)

temporario.replace(final)

Preservar permissões e proprietário

A substituição pode alterar permissões, ACLs ou proprietário. Em arquivos do sistema, capture e reaplique metadados conforme a política, ou utilize uma ferramenta de deploy apropriada.

Comparar plists semanticamente

Comparar bytes pode indicar diferença apenas por formato, ordem ou whitespace. Para comparação semântica, carregue ambos os arquivos e compare as estruturas Python.

a = plistlib.loads(arquivo_a)
b = plistlib.loads(arquivo_b)
assert a == b

Datas aware e ingênuas precisam ser normalizadas antes da comparação.

Converter XML para binário

with open("entrada.plist", "rb") as origem:
    dados = plistlib.load(origem)

with open("saida.plist", "wb") as destino:
    plistlib.dump(dados, destino, fmt=plistlib.FMT_BINARY)

Valide o modelo entre leitura e escrita. A conversão não deve ser usada para “limpar” arquivos que você ainda não confia.

Recursos empacotados

Templates plist distribuídos com um pacote podem ser lidos por importlib.resources, modificados em memória e gravados em um local apropriado do usuário.

Não tente escrever dentro do pacote instalado. Trate recursos empacotados como somente leitura.

Testes

Crie testes de ida e volta para XML e binário, datas, bytes, listas vazias, Unicode, inteiros extremos, chaves inválidas e arquivos malformados.

def test_round_trip():
    original = {"Nome": "Café", "Ativo": True}
    for formato in (plistlib.FMT_XML, plistlib.FMT_BINARY):
        restaurado = plistlib.loads(
            plistlib.dumps(original, fmt=formato)
        )
        assert restaurado == original

Compatibilidade

Se o arquivo será consumido por versões antigas de macOS, iOS ou outra linguagem, teste o artefato final na plataforma real. Nem toda ferramenta interpreta extensões ou estruturas específicas da mesma maneira.

Erros frequentes

  • Abrir o arquivo em modo texto.
  • Usar chaves que não são strings.
  • Ativar skipkeys e perder campos silenciosamente.
  • Misturar datetimes aware e ingênuos.
  • Tratar UID como objeto já resolvido.
  • Confiar em um plist apenas porque o parsing funcionou.
  • Gravar diretamente sobre configuração crítica.
  • Comparar bytes quando o objetivo é comparar dados.

Boas práticas

  • Abra arquivos em modo binário.
  • Valide o modelo após carregar.
  • Prefira aware_datetime=True para instantes.
  • Use XML para revisão e binário quando houver motivo.
  • Escreva de forma atômica.
  • Limite tamanho e profundidade de entradas externas.
  • Teste ida e volta em todos os formatos usados.
  • Não execute strings extraídas do arquivo.

Conclusão

O módulo plistlib no Python oferece suporte completo às variantes XML e binária de property lists, incluindo bytes, datas e UIDs. Ele é uma ferramenta prática para automação e interoperabilidade com o ecossistema Apple.

O parser valida o formato, não as regras da aplicação. Combine-o com limites, validação de esquema, política de timezone e escrita atômica. Consulte a documentação oficial de plistlib e a documentação de property lists da Apple para detalhes de compatibilidade.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Cadeado digital representando credenciais por host com netrc no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    netrc no Python: credenciais por host

    Aprenda netrc no Python para ler credenciais por host, validar permissões, tratar erros e integrar clientes de rede com segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    08/08/2026
    Mensagem digital representando codificação quoted-printable com quopri no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    quopri no Python: quoted-printable

    Aprenda quopri no Python para codificar e decodificar quoted-printable em e-mails, arquivos e integrações MIME com segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    08/08/2026
    Ícone de arquivo digital representando tipos MIME com mimetypes no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    mimetypes no Python: tipos MIME

    Aprenda mimetypes no Python para identificar tipos MIME, extensões e encodings com segurança em uploads, downloads e APIs web.

    Ler mais

    Tempo de leitura: 7 minutos
    08/08/2026
    Busca binária e listas ordenadas com bisect no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    bisect no Python: buscas em listas ordenadas

    Aprenda bisect no Python para buscar posições, inserir valores e trabalhar com duplicatas e faixas em listas ordenadas.

    Ler mais

    Tempo de leitura: 5 minutos
    08/08/2026
    Código e arquivos empacotados com importlib.resources no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    importlib.resources no Python: guia prático

    Aprenda importlib.resources no Python para acessar arquivos empacotados com segurança em pacotes, wheels e aplicações instaladas.

    Ler mais

    Tempo de leitura: 6 minutos
    07/08/2026
    Teclado e fluxo de dados representando leitura de vários arquivos com fileinput no Python
    Bibliotecas e Módulos
    Foto de perfil de Leandro Hirt da Academify

    fileinput no Python: leia vários arquivos

    Aprenda fileinput no Python para ler vários arquivos ou stdin, rastrear linhas, abrir gzip e reescrever conteúdo com backup e

    Ler mais

    Tempo de leitura: 8 minutos
    07/08/2026