Python é uma ótima linguagem de programação, mas a embalagem é um de seus pontos mais fracos. É um fato bem conhecido na comunidade. Instalar, importar, usar e criar pacotes melhorou muito ao longo dos anos, mas ainda não está no mesmo nível de linguagens mais recentes, como Go e Rust, que aprenderam muito com as dificuldades do Python e outras linguagens maduras.
Neste tutorial, você aprenderá tudo o que precisa saber sobre como escrever, empacotar e distribuir seus próprios pacotes.
Como escrever uma biblioteca Python
Uma biblioteca Python é uma coleção coerente de módulos Python organizados como um pacote Python. Em geral, isso significa que todos os módulos vivem no mesmo diretório e que esse diretório está no caminho de pesquisa do Python.
Vamos escrever rapidamente um pequeno pacote Python 3 e ilustrar todos esses conceitos.
O Pacote de Patologia
Python 3 tem um excelente Path
objeto, que é uma grande melhoria em relação ao desajeitado Python 2 os.path
módulo. Mas está faltando um recurso crucial – encontrar o caminho do script atual. Isso é muito importante quando você deseja localizar arquivos de acesso relativos ao script atual.
Em muitos casos, o script pode ser instalado em qualquer local, portanto, você não pode usar caminhos absolutos, e o diretório de trabalho pode ser definido com qualquer valor, portanto, não pode usar um caminho relativo. Se você deseja acessar um arquivo em um subdiretório ou diretório pai, deve ser capaz de descobrir o diretório de script atual.
Aqui está como você faz isso em Python:
import pathlib script_dir = pathlib.Path(__file__).parent.resolve()
Para acessar um arquivo chamado ‘file.txt’ em um subdiretório ‘data’ do diretório do script atual, você pode usar o seguinte código: print(open(str(script_dir/'data/file.txt').read())
Com o pacote de patologia, você tem um built-in script_dir método, e você o usa assim:
from pathology.Path import script_dir print(open(str(script_dir()/'data/file.txt').read())
Sim, é um bocado. O pacote de patologia é muito simples. Ele deriva sua própria classe Path do Path do pathlib e adiciona uma script_dir() que sempre retorna o caminho do script de chamada.
Aqui está a implementação:
import pathlib import inspect class Path(type(pathlib.Path())): @staticmethod def script_dir(): print(inspect.stack()[1].filename) p = pathlib.Path(inspect.stack()[1].filename) return p.parent.resolve()
Devido à implementação multiplataforma de pathlib.Path
você pode derivar diretamente dele e deve derivar de uma subclasse específica (PosixPath
ou WindowsPath
). o script_dir
A resolução usa o módulo de inspeção para localizar o chamador e, em seguida, seu atributo de nome de arquivo.
Testando o Pacote de Patologia
Sempre que você escreve algo que é mais do que um script descartável, você deve testá-lo. O módulo de patologia não é exceção. Aqui estão os testes usando a estrutura de teste de unidade padrão:
import os import shutil from unittest import TestCase from pathology.path import Path class PathTest(TestCase): def test_script_dir(self): expected = os.path.abspath(os.path.dirname(__file__)) actual = str(Path.script_dir()) self.assertEqual(expected, actual) def test_file_access(self): script_dir = os.path.abspath(os.path.dirname(__file__)) subdir = os.path.join(script_dir, 'test_data') if Path(subdir).is_dir(): shutil.rmtree(subdir) os.makedirs(subdir) file_path = str(Path(subdir)/'file.txt') content="123" open(file_path, 'w').write(content) test_path = Path.script_dir()/subdir/'file.txt' actual = open(str(test_path)).read() self.assertEqual(content, actual)
O caminho do Python
Os pacotes Python devem ser instalados em algum lugar no caminho de pesquisa do Python para serem importados pelos módulos do Python. O caminho de pesquisa do Python é uma lista de diretórios e está sempre disponível em sys.path
. Aqui está minha corrente sys.path
:
>>> print('n'.join(sys.path)) /Users/gigi.sayfan/miniconda3/envs/py3/lib/python36.zip /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6 /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/lib-dynload /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/site-packages /Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg
Observe que a primeira linha vazia da saída representa o diretório atual, então você pode importar módulos do diretório de trabalho atual, seja ele qual for. Você pode adicionar ou remover diretórios diretamente de/para sys.path.
Você também pode definir um PYTHONPATH
variável de ambiente e há algumas outras maneiras de controlá-la. O padrão site-packages
está incluído por padrão, e é aqui que os pacotes que você instala usando o pip go.
Como empacotar uma biblioteca Python
Agora que temos nosso código e testes, vamos empacotar tudo em uma biblioteca adequada. Python fornece uma maneira fácil através do módulo de configuração. Você cria um arquivo chamado setup.py no diretório raiz do seu pacote.
o setup.py files inclui muitas informações de metadados, como autor, licença, mantenedores e outras informações sobre o pacote. Isso além do packages
item que usa o find_packages()
função importada de setuptools
para encontrar subpacotes.
Aqui está o setup.py arquivo do pacote de patologia:
from setuptools import setup, find_packages setup(name="pathology", version='0.1', url="https://github.com/the-gigi/pathology", license="MIT", author="Gigi Sayfan", author_email="[email protected]", description='Add static script_dir() method to Path', packages=find_packages(exclude=['tests']), long_description=open('README.md').read(), zip_safe=False)
Pacote de distribuição de origem
Um pacote de distribuição de origem refere-se a um arquivo que contém pacotes python, módulos e outros arquivos que são usados para um lançamento de pacote (por exemplo, versão 1, 2 e assim por diante). Depois que o arquivo for distribuído, os usuários finais poderão baixá-lo e instalá-lo em seu sistema operacional.
Para criar um pacote de distribuição de origem (sdist), execute: python setup.py sdist
Vamos construir uma distribuição fonte:
$ python setup.py sdist running sdist running egg_info creating pathology.egg-info writing pathology.egg-info/PKG-INFO writing dependency_links to pathology.egg-info/dependency_links.txt writing top-level names to pathology.egg-info/top_level.txt writing manifest file 'pathology.egg-info/SOURCES.txt' reading manifest file 'pathology.egg-info/SOURCES.txt' writing manifest file 'pathology.egg-info/SOURCES.txt' warning: sdist: standard file not found: should have one of README, README.rst, README.txt running check creating pathology-0.1 creating pathology-0.1/pathology creating pathology-0.1/pathology.egg-info copying files to pathology-0.1... copying setup.py -> pathology-0.1 copying pathology/__init__.py -> pathology-0.1/pathology copying pathology/path.py -> pathology-0.1/pathology copying pathology.egg-info/PKG-INFO -> pathology-0.1/pathology.egg-info copying pathology.egg-info/SOURCES.txt -> pathology-0.1/pathology.egg-info copying pathology.egg-info/dependency_links.txt -> pathology-0.1/pathology.egg-info copying pathology.egg-info/not-zip-safe -> pathology-0.1/pathology.egg-info copying pathology.egg-info/top_level.txt -> pathology-0.1/pathology.egg-info Writing pathology-0.1/setup.cfg creating dist Creating tar archive removing 'pathology-0.1' (and everything under it)
O aviso é porque eu usei um não padrão README.md Arquivo. É seguro ignorar. O comando acima criará um arquivo compactado com o formato padrão para o sistema operacional atual. Para sistemas Unix, um arquivo tar compactado em gzip será gerado, sob a distância diretório:
$ ls -la dist total 8 drwxr-xr-x 3 gigi.sayfan gigi.sayfan 102 Apr 18 21:20 . drwxr-xr-x 12 gigi.sayfan gigi.sayfan 408 Apr 18 21:20 .. -rw-r--r-- 1 gigi.sayfan gigi.sayfan 1223 Apr 18 21:20 pathology-0.1.tar.gz
Se você estiver usando o Windows, um arquivo zip será gerado.
Você também pode especificar outros formatos de arquivo adicionais usando a opção de formato da seguinte maneira.
python setup.py sdist --formats=gztar,zip
Por exemplo, o comando acima irá gerar um tarball compactado em gzip e um arquivo zip.
Os diferentes formatos disponíveis são:
-
zip
: .fecho eclair -
gztar
: .tar.gz -
bztar
: .tar.bz2 -
xztar
: .tar.xz -
ztar
: .tar.Z -
tar
: .alcatrão
Distribuição Binária
Para criar uma distribuição binária, chamada roda, você executa: python setup.py bdist_wheel
E aqui está uma distribuição binária:
$ python setup.py bdist_wheel running bdist_wheel running build running build_py creating build creating build/lib creating build/lib/pathology copying pathology/__init__.py -> build/lib/pathology copying pathology/path.py -> build/lib/pathology installing to build/bdist.macosx-10.7-x86_64/wheel running install running install_lib creating build/bdist.macosx-10.7-x86_64 creating build/bdist.macosx-10.7-x86_64/wheel creating build/bdist.macosx-10.7-x86_64/wheel/pathology copying build/lib/pathology/__init__.py -> build/bdist.macosx-10.7-x86_64/wheel/pathology copying build/lib/pathology/path.py -> build/bdist.macosx-10.7-x86_64/wheel/pathology running install_egg_info running egg_info writing pathology.egg-info/PKG-INFO writing dependency_links to pathology.egg-info/dependency_links.txt writing top-level names to pathology.egg-info/top_level.txt reading manifest file 'pathology.egg-info/SOURCES.txt' writing manifest file 'pathology.egg-info/SOURCES.txt' Copying pathology.egg-info to build/bdist.macosx-10.7-x86_64/wheel/pathology-0.1-py3.6.egg-info running install_scripts creating build/bdist.macosx-10.7-x86_64/wheel/pathology-0.1.dist-info/WHEEL
O pacote pathology contém apenas módulos Python puros, portanto, um pacote universal pode ser construído. Se o seu pacote inclui extensões C, você terá que construir uma roda separada para cada plataforma:
$ ls -la dist total 16 drwxr-xr-x 4 gigi.sayfan gigi.sayfan 136 Apr 18 21:24 . drwxr-xr-x 13 gigi.sayfan gigi.sayfan 442 Apr 18 21:24 .. -rw-r--r-- 1 gigi.sayfan gigi.sayfan 2695 Apr 18 21:24 pathology-0.1-py3-none-any.whl -rw-r--r-- 1 gigi.sayfan gigi.sayfan 1223 Apr 18 21:20 pathology-0.1.tar.gz
Para um mergulho mais profundo no tópico de empacotar bibliotecas Python, confira Como escrever seus próprios pacotes Python.
Como distribuir um pacote Python
Python tem um repositório central de pacotes chamado PyPI (Python Packages Index). O PyPI facilita o gerenciamento de diferentes versões de pacotes. Por exemplo, se um usuário precisar instalar uma versão de pacote específica, o pip sabe onde procurá-la.
Quando você instala um pacote Python usando pip, ele baixa o pacote do PyPI (a menos que você especifique um repositório diferente). Para distribuir nosso pacote de patologia, precisamos carregá-lo no PyPI e fornecer alguns metadados extras que o PyPI requer. Os passos são:
- Atualize sua versão pip.
- Crie uma conta no PyPI (apenas uma vez).
- Registre seu pacote.
- Carregue seu pacote.
Atualize sua versão do pip
Certifique-se de ter a versão mais recente do pip instalada em seu sistema operacional. Para atualizar o pip, emita o seguinte comando
python3 -m pip install --upgrade pip
Crie a sua conta aqui
Você pode criar uma conta no site PyPI. Em seguida, crie um .pypirc arquivo em seu diretório inicial:
[distutils] index-servers=pypi [pypi] repository = https://pypi.python.org/pypi username = the_gigi
Para fins de teste, você pode adicionar um pypitest
servidor de indexação para o seu arquivo .pypirc Arquivo:
[distutils] index-servers= pypi pypitest [pypitest] repository = https://testpypi.python.org/pypi username = the_gigi [pypi] repository = https://pypi.python.org/pypi username = the_gigi
Registre seu pacote
Se esta for a primeira versão do seu pacote, você precisa registrá-lo no PyPI. Use o comando de registro de setup.py. Ele vai pedir sua senha. Observe que eu aponto para o repositório de teste aqui:
$ python setup.py register -r pypitest running register running egg_info writing pathology.egg-info/PKG-INFO writing dependency_links to pathology.egg-info/dependency_links.txt writing top-level names to pathology.egg-info/top_level.txt reading manifest file 'pathology.egg-info/SOURCES.txt' writing manifest file 'pathology.egg-info/SOURCES.txt' running check Password: Registering pathology to https://testpypi.python.org/pypi Server response (200): OK
Carregue seu pacote
Agora que o pacote está registrado, podemos carregá-lo. Eu recomendo usar barbante, que é mais seguro. Instale-o normalmente usando pip install twine
. Em seguida, faça o upload do seu pacote usando barbante e forneça sua senha (redigido abaixo):
$ twine upload -r pypitest -pdist/* Uploading distributions to https://testpypi.python.org/pypi Uploading pathology-0.1-py3-none-any.whl [================================] 5679/5679 - 00:00:02 Uploading pathology-0.1.tar.gz [================================] 4185/4185 - 00:00:01
O pacote já está disponível no site oficial do Pypi, conforme mostrado abaixo.
Para instalá-lo com pip, basta emitir o seguinte comando:
pip install pathology
Para um mergulho mais profundo no tópico de distribuição de seus pacotes, confira Como compartilhar seus pacotes Python.
Conclusão
Neste tutorial, passamos pelo processo completo de escrever uma biblioteca Python, empacotá-la e distribuí-la por meio do PyPI. Neste ponto, você deve ter todas as ferramentas para escrever e compartilhar suas bibliotecas com o mundo.
Este post foi atualizado com contribuições de Esther Vaati. Esther é desenvolvedora de software e escritora da Envato Tuts+.