# `importlib.metadata` – Acessando metadados do pacote

Adicionado na versão 3.8.

Alterado na versão 3.10: `importlib.metadata` não é mais provisório.

Código-fonte: Lib/importlib/metadata/__init__.py

`importlib.metadata` é uma biblioteca que fornece acesso aos metadados de um Pacote de Distribuição instalado, como seus pontos de entrada ou seus nomes de nível superior (Pacotes de Importação <https://packaging.python.org/pt_BR/latest/glossary/#term-Import-Package>, módulos, se houver). Construída em parte no sistema de importação do Python, esta biblioteca pretende substituir funcionalidades semelhantes na API de ponto de entrada e API de metadados de `pkg_resources`. Junto com `importlib.resources`, este pacote pode eliminar a necessidade de usar o pacote `pkg_resources` mais antigo e menos eficiente.

`importlib.metadata` opera em pacotes de distribuição de terceiros instalados no diretório `site-packages` do Python através de ferramentas como pip. Especificamente, ele funciona com distribuições com diretórios `dist-info` ou `egg-info` detectáveis, e metadados definidos pelas Especificações de metadados principais.

Importante

Eles não são necessariamente equivalentes ou correspondem 1:1 aos nomes de pacotes de importação de nível superior que podem ser importados dentro do código Python. Um pacote de distribuição pode conter vários pacotes de importação (e módulos únicos), e um pacote de importação de nível superior pode ser mapeado para vários pacotes de distribuição se for um pacote de espaço de nomes. Você pode usar package_distributions() para obter um mapeamento entre eles.

Por padrão, os metadados de distribuição podem residir no sistema de arquivos ou em arquivos zip em `sys.path`. Através de um mecanismo de extensão, os metadados podem residir em praticamente qualquer lugar.

Ver também

https://importlib-metadata.readthedocs.io/

  A documentação para `importlib_metadata`, que fornece um backport de `importlib.metadata`. Isso inclui uma referência de API para as classes e funções deste módulo, bem como um guia de migração para usuários existentes de `pkg_resources`.

## Visão Geral

Digamos que você queira obter a string da versão de um Pacote de Distribuição que você instalou usando `pip`. Começamos criando um ambiente virtual e instalando algo nele:

    $ python -m venv example
    $ source example/bin/activate
    (example) $ python -m pip install wheel

Você pode obter a string de versão para `wheel` executando o seguinte:

    (example) $ python
    >>> from importlib.metadata import version
    >>> version('wheel')
    '0.32.3'

Você também pode obter uma coleção de pontos de entrada selecionáveis pelas propriedades do EntryPoint (normalmente ‘group’ ou ‘name’), como `console_scripts`, `distutils.commands` e outros. Cada grupo contém uma coleção de objetos EntryPoint.

Você pode obter os metadados para uma distribuição:

    >>> list(metadata('wheel'))
    ['Metadata-Version', 'Name', 'Version', 'Summary', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', 'Project-URL', 'Project-URL', 'Project-URL', 'Keywords', 'Platform', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Requires-Python', 'Provides-Extra', 'Requires-Dist', 'Requires-Dist']

Você também pode obter uma número da versão da distribuição, listar seus arquivos constituintes e obter uma lista dos Requisitos de distribuição da distribuição.

## API funcional

Este pacote fornece a seguinte funcionalidade por meio de sua API pública.

### Pontos de entrada

A função `entry_points()` retorna uma coleção de pontos de entrada. Os pontos de entrada são representados por instâncias `EntryPoint`; cada `EntryPoint` tem atributos `.name`, `.group` e `.value` e um método `.load()` para resolver o valor. Existem também atributos `.module`, `.attr` e `.extras` para obter os componentes do atributo `.value`.

Consultar todos os pontos de entrada:

    >>> eps = entry_points()

A função `entry_points()` retorna um objeto `EntryPoints`, uma coleção de todos os objetos `EntryPoint` com atributos `names` e `groups` por conveniência:

    >>> sorted(eps.groups)
    ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']

`EntryPoints` possui um método `select` para selecionar pontos de entrada que correspondam a propriedades específicas. Selecione pontos de entrada no grupo `console_scripts`:

    >>> scripts = eps.select(group='console_scripts')

Equivalentemente, já que `entry_points` passa argumento nomeado para seleção:

    >>> scripts = entry_points(group='console_scripts')

Escolha um script específico chamado “wheel” (encontrado no projeto wheel):

    >>> 'wheel' in scripts.names
    True
    >>> wheel = scripts['wheel']

De forma equivalente, consulte esse ponto de entrada durante a seleção:

    >>> (wheel,) = entry_points(group='console_scripts', name='wheel')
    >>> (wheel,) = entry_points().select(group='console_scripts', name='wheel')

Inspecione o ponto de entrada resolvido:

    >>> wheel
    EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')
    >>> wheel.module
    'wheel.cli'
    >>> wheel.attr
    'main'
    >>> wheel.extras
    []
    >>> main = wheel.load()
    >>> main
    <function main at 0x103528488>

O `group` e `name` são valores arbitrários definidos pelo autor do pacote e normalmente um cliente desejará resolver todos os pontos de entrada para um grupo específico. Leia a documentação do setuptools para obter mais informações sobre pontos de entrada, sua definição e uso.

Nota de Compatibilidade

Os pontos de entrada “selecionáveis” foram introduzidos em `importlib_metadata` 3.6 e Python 3.10. Antes dessas mudanças, `entry_points` não aceitava parâmetros e sempre retornava um dicionário de pontos de entrada, chaveado por grupo. Com `importlib_metadata` 5.0 e Python 3.12, `entry_points` sempre retorna um objeto `EntryPoints`. Veja backports.entry_points_selectable para opções de compatibilidade.

### Metadados de distribuição

Cada Pacote de Distribuição inclui alguns metadados, que você pode extrair usando a função `metadata()`:

    >>> wheel_metadata = metadata('wheel')

As chaves da estrutura de dados retornada, um `PackageMetadata`, nomeiam as palavras reservadas dos metadados, e os valores são retornados sem análise dos metadados de distribuição:

    >>> wheel_metadata['Requires-Python']
    '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'

`PackageMetadata` também apresenta um atributo `json` que retorna todos os metadados em um formato compatível com JSON pela PEP 566:

    >>> wheel_metadata.json['requires_python']
    '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'

Nota

O tipo real do objeto retornado por `metadata()` é um detalhe de implementação e deve ser acessado somente através da interface descrita pelo protocolo PackageMetadata.

Alterado na versão 3.10: A `Description` agora é incluída nos metadados quando apresentada através do payload. Os caracteres de continuação de linha foram removidos.

O atributo `json` foi adicionado.

### Versões de distribuição

A função `version()` é a maneira mais rápida de obter o número de versão de um Pacote de Distribuição, como uma string:

    >>> version('wheel')
    '0.32.3'

### Arquivos de distribuição

Você também pode obter o conjunto completo de arquivos contidos em uma distribuição. A função `files()` pega um nome Pacote de Distribuição e retorna todos os arquivos instalados por este distribuição. Cada objeto de arquivo retornado é um `PackagePath`, um objeto derivado de `pathlib.PurePath` com propriedades adicionais `dist`, `size` e `hash` conforme indicado pelos metadados. Por exemplo:

    >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0]
    >>> util
    PackagePath('wheel/util.py')
    >>> util.size
    859
    >>> util.dist
    <importlib.metadata._hooks.PathDistribution object at 0x101e0cef0>
    >>> util.hash
    <FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>

Uma vez que tenha o arquivo, você também pode ler seu conteúdo:

    >>> print(util.read_text())
    import base64
    import sys
    ...
    def as_bytes(s):
        if isinstance(s, text_type):
            return s.encode('utf-8')
        return s

Você também pode usar o método `locate` para obter o caminho absoluto para o arquivo:

    >>> util.locate()
    PosixPath('/home/gustav/example/lib/site-packages/wheel/util.py')

No caso em que o arquivo de metadados que lista os arquivos (RECORD ou SOURCES.txt) estiver faltando, `files()` retornará `None`. O chamador pode querer agrupar chamadas para `files()` em always_iterable ou de outra forma se proteger contra isso condição se a distribuição de destino não for conhecida por ter os metadados presentes.

### Requisitos de distribuição

Para obter o conjunto completo de requisitos para um Pacote de Distribuição, use a função `requires()`:

    >>> requires('wheel')
    ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]

### Mapeando importação pra pacotes de distribuição

Um método conveniente para resolver o nome do Pacote de Distribuição (ou nomes, no caso de um pacote de espaço de nomes) que fornece cada módulo Python de nível superior importável ou Pacote de Importação:

    >>> packages_distributions()
    {'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}

Algumas instalações editáveis não fornecem nomes de nível superior e, portanto, esta função não é confiável com tais instalações.

Adicionado na versão 3.10.

## Distribuições

Embora a API acima seja o uso mais comum e conveniente, você pode obter todas essas informações na classe `Distribution`. Uma `Distribution` é um objeto abstrato que representa os metadados de um Pacote de Distribuição do Python. Você pode obter a instância `Distribution`:

    >>> from importlib.metadata import distribution
    >>> dist = distribution('wheel')

Assim, uma forma alternativa de obter o número da versão é através da instância `Distribution`:

    >>> dist.version
    '0.32.3'

Existem todos os tipos de metadados adicionais disponíveis na instância `Distribution`:

    >>> dist.metadata['Requires-Python']
    '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
    >>> dist.metadata['License']
    'MIT'

O conjunto completo de metadados disponíveis não é descrito aqui. Consulte as Especificações de metadados principais para obter detalhes adicionais.

## Descoberta de distribuição

Por padrão, este pacote fornece suporte embutido para descoberta de metadados para sistema de arquivos e arquivo zip Pacotes de Distribuição <https://packaging.python.org/pt_BR/latest/glossary/#term-Distribution-Package>. Esta pesquisa do localizador de metadados tem como padrão `sys.path`, mas varia um pouco na maneira como ela interpreta esses valores em relação a outras mecanismo de importação. Em particular:

- `importlib.metadata` não honra objetos `bytes` em `sys.path`.

- `importlib.metadata` irá incidentalmente honrar objetos `pathlib.Path` em `sys.path` mesmo que tais valores sejam ignorados para importações.

## Estendendo o algoritmo de pesquisa

Como os metadados de Pacote de Distribuição não estão disponíveis por meio de pesquisas a `sys.path` ou carregadores de pacotes diretamente, os metadados de uma distribuição são encontrados através de localizadores do sistema de importação. Para encontrar os metadados de um pacote de distribuição, `importlib.metadata` consulta a lista de localizadores de metacaminho em `sys.meta_path`.

Por padrão `importlib.metadata` instala um localizador para pacotes de distribuição encontrados no sistema de arquivos. Na verdade, esse localizador não encontra nenhuma distribuição, mas pode encontrar seus metadados.

A classe abstrata `importlib.abc.MetaPathFinder` define a interface esperada dos localizadores pelo sistema de importação do Python. `importlib.metadata` estende este protocolo procurando por um chamável `find_distributions` opcional nos localizadores de `sys.meta_path` e apresenta esta interface estendida como a classe base abstrata `DistributionFinder`, que define este método abstrato:

    @abc.abstractmethod
    def find_distributions(context=DistributionFinder.Context()):
        """Return an iterable of all Distribution instances capable of
        loading the metadata for packages for the indicated ``context``.
        """

O objeto `DistributionFinder.Context` fornece propriedades `.path` e `.name` indicando o caminho para pesquisar e o nome a ser correspondido e pode fornecer outro contexto relevante.

O que isso significa na prática é que para prover suporte à localização de metadados de pacotes de distribuição em outros locais fora do sistema de arquivos, crie uma subclasse `Distribution` e implemente os métodos abstratos. Então, a partir de um localizador personalizado, retorne instâncias deste derivado de `Distribution` no método `find_distributions()`.