Debugando Python dockerizado no PyCharm
print()
my beloved
Eu passei um bom tempo usando print()
para debugar código Python que estava rodando em containers por não saber
como usar o debugger de maneira adequada (dã). Acho que descobri.
Como eu utilizo o PyCharm (e talvez você devesse também), esse tutorial não cobre VS Code ou pdb puro.
Sigamos.
Caso simples: Uma app boba
Digamos que você tem uma web app Flask super complexa:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
my_return = "Hello, World!"
return my_return
if __name__ == '__main__':
app.run(host="0.0.0.0")
Dockerfile:
FROM python:3.10
WORKDIR /
COPY . .
RUN pip3 install -r requirements.txt
CMD [ "python3", "./main.py"]
docker-compose.yml:
version: "3.5"
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "5000:5000"
- Vou utilizar um docker-compose.yml por ser útil para mapear portas e para adicionar outros serviços (ex.: bd), como veremos adiante.
Bom, se tentarmos debugar utilizando o interpretador que está aí no seu venv (pf esteja usando um venv), vai funcionar, porém, não estaríamos debugando a aplicação rodando no container e sim localmente. Temos que especificar um interpretador que esteja dentro do container que será debugado.
Add novo interpretador “Docker Compose”
- Se preferir: menu > settings > project > python interpreter > add new interpreter
Especifique o caminho do seu docker-compose.yml.
Depois, escolha o nome do que deseja debugar, especificando o mesmo nome que foi utilizado no
docker-compose.yml. No nosso caso é app. Clique next
.
- Poderiam haver outros serviços como bancos de dados ou caches que não seriam alvo de debug
Por último, escolhemos o interpretador. System Interpreter > /usr/local/bin/python3
, clique create
.
- Deixamos na opção System Interpreter pois não tem necessidade de usar um venv dentro do container.
Ok, interpretador criado!
O próximo passo é criar uma configuração de run/debug, para rodar nosso programa utilizando o interpretador que acabamos de criar. Láá em cima, do lado da baratinha:
Clique no “+” e crie um novo perfil “Python”
Escolha o interpretador que foi criado e especifique o entrypoint da sua aplicação. Nesse caso é o próprio main.py
,
com o tipo “script”. Clique Apply
- Poderia ser um perfil “Flask”, mas para ser mais genérico vamos de “Python”
🤗Pronto🤗
Agora, coloque seus breakpoints e clique na baratinha para debugar.
Caso menos simples: Uma app boba + um banco de dados
Bom, digamos que existam outros serviços adjacentes à sua aplicação, como, por exemplo, um banco de dados.
docker-compose.yml:
version: "3.5"
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "5000:5000"
mongo:
extends:
file: mongo.yml
service: mongo
volumes:
- mongo_data:/data/db
volumes:
mongo_data:
mongo.yml:
version: "3.5"
services:
mongo:
image: mongo:5
command: mongod --port 27017
restart: always
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: password
Nesse caso, se seguirmos os passos da seção anterior, nosso banco de dados nunca será executado, pois o interpretador configurado só diz respeito à aplicação em si (app), e a configuração de run/debug também, e apenas executa nosso programa a partir de um entrypoint pré-estabelecido (main.py).
Criaremos então outra configuração de run/debug. Assim como antes, abra a aba de run/debug, e dessa vez, clique no “+” e escolha uma config do tipo “Docker Compose”.
Especifique o caminho do arquivo “docker-compose.yml”. Note, no campo “Services” está escrito: “Leave empty to run all services” (Deixe em branco para rodar todos os serviços).
Ou seja, podemos deixar o campo em branco para que essa config de run/debug rode os dois serviços (app e mongo), ou, especificar apenas o serviço mongo, uma vez que não podemos “debugar” a app utilizando essa config do tipo “Docker Compose”. Para debugar nossa app de fato, utilizando breakpoints e tudo mais, é necessário utilizar a config criada anteriormente que especifica o interpretador Dockerizado (criado lá no início) e o entrypoint (main.py nesse caso).
Porém, não é problema deixarmos essa nova config de run/debug iniciar os dois serviços e depois executarmos a config criada anteriormente para então debugar nossa aplicação. Por isso, pode deixar em branco, ou especificar apenas mongo na aba “Services”.
Ok, agora é só rodar (clicando na setinha do lado da barata) essa nova config para subir o mongo + app (ou só mongo), e depois rodar a config que criamos anteriormente para então debugar nossa aplicação. Lembre de usar a baratinha
- A app acaba nem chamando o mongo… mas poderia.
Debugando testes com Pytest
Se você precisa debugar seus testes, crie uma nova config de run/debug do tipo
“Pytest”. Escolha o interpretador criado anteriormente (aquele do container), e, se você estiver utilizando pytest-cov
no seu projeto, no campo “Additional Arguments” coloque --no-cov
.
Pronto, agora pode colocar seus breakpoints nos testes e debugar feliz da vida.
Como estou dirigindo?
Se você tem alguma sugestão, dica, ou encontrou algum erro, pode deixar nos comentários abaixo. (:
bye bye