Um prelúdio de embalagem:
Antes mesmo de se preocupar com a leitura de arquivos de recursos, a primeira etapa é certificar-se de que os arquivos de dados estão sendo empacotados em sua distribuição - é fácil lê-los diretamente da árvore de origem, mas a parte importante é fazer certifique-se de que esses arquivos de recursos sejam acessíveis a partir do código de um pacote instalado .
Estruture seu projeto assim, colocando os arquivos de dados em um subdiretório dentro do pacote:
.
├── package
│ ├── __init__.py
│ ├── templates
│ │ └── temp_file
│ ├── mymodule1.py
│ └── mymodule2.py
├── README.rst
├── MANIFEST.in
└── setup.py
Você deve passar include_package_data=Truena setup()chamada. O arquivo de manifesto é necessário apenas se você quiser usar setuptools / distutils e distribuições de código-fonte de compilação. Para garantir que o templates/temp_fileseja empacotado para esta estrutura de projeto de exemplo, adicione uma linha como esta no arquivo de manifesto:
recursive-include package *
Nota crítica histórica: o uso de um arquivo de manifesto não é necessário para back-ends de compilação moderna , como flit, poesia, que incluirá os arquivos de dados do pacote por padrão. Portanto, se você estiver usando pyproject.tomle não tiver um setup.pyarquivo, poderá ignorar todas as informações sobre MANIFEST.in.
Agora, com a embalagem fora do caminho, para a parte de leitura ...
Recomendação:
Use pkgutilAPIs de biblioteca padrão . Vai ficar assim no código da biblioteca:
# within package/mymodule1.py, for example
import pkgutil
data = pkgutil.get_data(__name__, "templates/temp_file")
print("data:", repr(data))
text = pkgutil.get_data(__name__, "templates/temp_file").decode()
print("text:", repr(text))
Funciona em zips. Funciona em Python 2 e Python 3. Não requer dependências de terceiros. Não estou realmente ciente de quaisquer desvantagens (se você estiver, por favor, comente sobre a resposta).
Maneiras ruins de evitar:
Maneira ruim nº 1: usando caminhos relativos de um arquivo de origem
Esta é atualmente a resposta aceita. Na melhor das hipóteses, é mais ou menos assim:
from pathlib import Path
resource_path = Path(__file__).parent / "templates"
data = resource_path.joinpath("temp_file").read_bytes()
print("data", repr(data))
O que há de errado nisso? A suposição de que você possui arquivos e subdiretórios disponíveis não é correta. Essa abordagem não funciona se estiver executando um código compactado em um zip ou roda, e pode estar totalmente fora do controle do usuário, independentemente de seu pacote ser extraído ou não para um sistema de arquivos.
Maneira ruim nº 2: usando APIs pkg_resources
Isso é descrito na resposta mais votada. É mais ou menos assim:
from pkg_resources import resource_string
data = resource_string(__name__, "templates/temp_file")
print("data", repr(data))
O que há de errado nisso? Ele adiciona uma dependência de tempo de execução em ferramentas de instalação , que deve ser preferencialmente apenas uma dependência de tempo de instalação . A importação e o uso pkg_resourcespodem se tornar muito lentos, pois o código constrói um conjunto de trabalho de todos os pacotes instalados, mesmo que você esteja interessado apenas em seus próprios recursos de pacote. Isso não é grande coisa no momento da instalação (já que a instalação é única), mas é feio no momento da execução.
Maneira ruim nº 3: usando APIs importlib.resources
Esta é atualmente a recomendação na resposta mais votada. É uma adição recente à biblioteca padrão ( nova no Python 3.7 ), mas também está disponível uma porta traseira. Se parece com isso:
try:
from importlib.resources import read_binary
from importlib.resources import read_text
except ImportError:
# Python 2.x backport
from importlib_resources import read_binary
from importlib_resources import read_text
data = read_binary("package.templates", "temp_file")
print("data", repr(data))
text = read_text("package.templates", "temp_file")
print("text", repr(text))
O que há de errado nisso? Bem, infelizmente, não funciona ... ainda. Esta ainda é uma API incompleta, o uso importlib.resourcesexigirá que você adicione um arquivo vazio templates/__init__.pypara que os arquivos de dados residam em um subpacote em vez de em um subdiretório. Ele também irá expor o package/templatessubdiretório como um subpacote importável package.templatespor conta própria. Se isso não é um grande problema e não o incomoda, então você pode ir em frente e adicionar o __init__.pyarquivo lá e usar o sistema de importação para acessar os recursos. No entanto, enquanto você está nisso, você também pode transformá-lo em um my_resources.pyarquivo e apenas definir alguns bytes ou variáveis de string no módulo e importá-los no código Python. É o sistema de importação que está fazendo o trabalho pesado aqui de qualquer maneira.
Projeto de exemplo:
Criei um projeto de exemplo no github e carreguei no PyPI , que demonstra todas as quatro abordagens discutidas acima. Experimente com:
$ pip install resources-example
$ resources-example
Veja https://github.com/wimglenn/resources-example para mais informações.