Introdução parte 4
string.Template
O string.Template
é uma classe da biblioteca padrão usada para fazer substituição de variáveis dentro de strings de forma simples e segura. Ela pertence ao módulo string
e oferece uma forma alternativa ao uso de f-strings
ou .format()
, especialmente útil em casos onde você quer permitir que usuários definam templates de forma controlada, ou quando quer evitar a execução acidental de código (como pode acontecer com eval
ou f-strings).
É uma ótima maneira para trabalhar com textos vindos de fontes externas (como arquivos de configuração ou entradas de usuário). Evita a execução de expressões Python embutidas (ao contrário de f""
ou .format()
).
from string import Template
template = Template("Olá, $nome! Seja bem-vindo ao $local.")
mensagem = template.substitute(nome="Fulano", local="sistema")
print(mensagem)
Saída:
Olá, Fulano! Seja bem-vindo ao sistema.
Uma váriavel deve começar com $
(como $nome
). Se o nome da variável for ambíguo, você pode usar chaves: ${nome}
.
Exemplo com número depois da variável (evita confusão):
t = Template("Valor: $var10 e não ${var}10")
substitute vs safe_substitute
O substitute
aciona um erro (KeyError
) se alguma variável estiver faltando:
Template("Olá, $nome").substitute() # KeyError
Já safe_substitute
, vai ignora variáveis não encontradas e as deixa como estão:
Template("Olá, $nome").safe_substitute() # "Olá, $nome"
Exemplo com dicionário
dados = {"usuario": "Ana", "plano": "Premium"}
t = Template("Usuário: $usuario\nPlano: $plano")
print(t.substitute(dados))
Exemplo prático com arquivo
Suponha que você tenha um arquivo chamado mensagem.txt
com o seguinte conteúdo:
Olá, $nome!
Seu pedido de número $pedido foi enviado para o endereço: $endereco.
Agradecemos por comprar com a gente!
Agora você pode fazer a substituição assim:
from string import Template
# Lê o conteúdo do arquivo
with open("mensagem.txt", "r", encoding="utf-8") as f:
conteudo = f.read()
# Cria o template a partir do texto do arquivo
template = Template(conteudo)
# Define os dados para substituir
dados = {
"nome": "Fulano",
"pedido": "123456",
"endereco": "Rua dos Exemplos, 123"
}
# Faz a substituição
mensagem_final = template.substitute(dados)
print(mensagem_final)
Resultado:
Olá, Fulano!
Seu pedido de número 123456 foi enviado para o endereço: Rua dos Exemplos, 123.
Agradecemos por comprar com a gente!
Se o arquivo pode ter variáveis que talvez não estejam no dicionário, use safe_substitute
para evitar erro!
dotenv
O dotenv (.env
) é um padrão (e também um pacote) usado para carregar variáveis de ambiente a partir de um arquivo .env
. É muito comum em projetos Python (e também em outras linguagens, como Node.js e PHP) para separar configurações sensíveis ou dependentes do ambiente, como senhas, tokens, nomes de banco de dados e portas.
Essas variáveis são então carregadas no os.environ
, que é o dicionário de variáveis de ambiente do Python.
O dotenv .env
serve para guardar valores sensíveis, secretos ou específicos de cada ambiente (dev, staging, produção), sem deixar esses dados no código-fonte. Por exemplo:
cat .env
API_KEY=abc123
DB_USER=admin
DB_PASS=senha123
DEBUG=true
Como usar no Python
Instale o pacote:
pip3 install python-dotenv
Depois, no seu código Python:
import os
from dotenv import load_dotenv
load_dotenv() # Procura e carrega o arquivo .env na raiz
api_key = os.getenv("API_KEY")
debug = os.getenv("DEBUG") == "true"
print(api_key)
print(debug)
Adicione o .env
ao .gitignore
:
.env
E se precisar, crie um arquivo .env.example
com as variáveis que devem ser preenchidas manualmente.
SMTP com GMAIL
Enviar emails via SMTP com o Gmail é possível em Python usando a biblioteca padrão smtplib
, mas há cuidados de segurança importantes, principalmente por conta das restrições do Google em conexões menos seguras.
Abaixo estão os passos atualizados para 2025, já que o Google não permite mais autenticação com senha comum para SMTP em contas padrão. Para enviar e-mails pelo Gmail, você deve usar:
- Senha de app (se 2FA estiver ativado)
- Ou configurar um servidor SMTP autenticado com OAuth2 (mais complexo)
Ative a verificação em duas etapas na sua conta Gmail.
Gere uma senha de app no painel de segurança do Google: https://myaccount.google.com/apppasswords
- Escolha "Mail" como o app e "Other" para dar um nome (ex: "Script Python").
- Copie a senha gerada (uma sequência de 16 caracteres).
Exemplo em Python
import smtplib
from email.message import EmailMessage
# Cria a mensagem
msg = EmailMessage()
msg['Subject'] = 'Teste via SMTP Gmail'
msg['From'] = 'seuemail@gmail.com'
msg['To'] = 'destinatario@exemplo.com'
msg.set_content('Olá, este é um e-mail de teste enviado com Python!')
# Conecta e envia
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login('seuemail@gmail.com', 'SENHA_DE_APP')
smtp.send_message(msg)
- Erros comuns
- 535 Authentication failed → Senha incorreta ou 2FA não ativado.
- "Less secure apps" bloqueado → O Google bloqueou esse recurso desde 2022.
- Connection timed out → Firewall, antivírus ou proxy bloqueando o acesso à porta 465.
Usando seu próprio servidor de email com login
Se você tem seu próprio servidor SMTP (por exemplo, Postfix, Exim ou outro), e precisa autenticar nele via Python, o processo com smtplib
é muito semelhante ao do Gmail, mas com menos restrições.
A autenticação no SMTP segue o padrão do protocolo (RFC 4954), e normalmente usa o mecanismo PLAIN, LOGIN ou CRAM-MD5.
import smtplib
from email.message import EmailMessage
# Cria a mensagem
msg = EmailMessage()
msg['Subject'] = 'Teste via SMTP local'
msg['From'] = 'usuario@seudominio.com.br'
msg['To'] = 'alguem@destino.com'
msg.set_content('Este é um email enviado a partir de um servidor SMTP próprio com autenticação.')
# Conecta e autentica no servidor SMTP
with smtplib.SMTP('mail.seudominio.com.br', 587) as smtp:
smtp.starttls() # ativa TLS (STARTTLS)
smtp.login('usuario@seudominio.com.br', 'SENHA')
smtp.send_message(msg)
- Porta 587 aberta (para STARTTLS) ou porta 465 (para SMTPS).
- Suporte a autenticação SASL (como LOGIN ou PLAIN).
- Um certificado TLS válido (ou aceitar conexões STARTTLS com auto-assinados, mas é menos recomendado).
- Usuário e senha criados corretamente no seu MTA (via SASL, Dovecot, LDAP, etc.).
SMTPS vs STARTTLS
O SMTP com STARTTLS usa a porta 587, começa como conexão simples e sobe para TLS com .starttls()
. Já o SMTP com SSL direto, usa a porta 465, a conexão é segura desde o início com SMTP_SSL()
.
Exemplo com SMTPS (porta 465):
with smtplib.SMTP_SSL('mail.seudominio.com.br', 465) as smtp:
smtp.login('usuario@seudominio.com.br', 'SENHA')
smtp.send_message(msg)
Para testar manualmente seu servidor SMTP:
openssl s_client -starttls smtp -connect mail.seudominio.com.br:587
Ou:
openssl s_client -connect mail.seudominio.com.br:465
Usando seu próprio servidor de email sem login
Se você quer enviar e-mails a partir do seu próprio servidor (localhost) sem autenticação (ou seja, sem precisar passar usuário e senha), isso é possível, mas depende da configuração do seu MTA (ex: Postfix, Exim ou Sendmail).
Nesse caso, você estará utilizando o servidor SMTP local como "relay direto", o que é comum em servidores Linux que enviam notificações internas, logs ou relatórios automáticos.
- O servidor SMTP local deve estar configurado para aceitar conexões locais (localhost/127.0.0.1).
- Não deve exigir autenticação para conexões locais (SASL desabilitado para
127.0.0.1
). - Você precisa configurar corretamente o domínio de origem e o
from
para evitar bloqueios por SPF, DKIM e PTR (se for enviar para a internet).
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Relatório do sistema'
msg['From'] = 'root@localhost'
msg['To'] = 'voce@exemplo.com'
msg.set_content('Tudo está funcionando corretamente.')
with smtplib.SMTP('localhost', 25) as smtp:
smtp.send_message(msg)
Esse código não faz login e usa a conexão local padrão na porta 25.
Segurança
Nunca exponha a senha no código-fonte. Embora seja comum o uso de os.getenv()
com arquivos .env
, essa prática só é minimamente aceitável em ambientes controlados (como containers). Ainda assim, ela apresenta riscos, pois variáveis de ambiente podem ser lidas por outros processos.
Em ambientes mais sensíveis, o ideal é utilizar um gerenciador de segredos adequado (como Docker Secrets, Vault, ou serviços de nuvem), garantindo que as credenciais estejam fora do alcance de acessos indevidos.
http.server
O http.server
é um módulo da biblioteca padrão do Python que permite subir um servidor HTTP simples, ideal para testes rápidos ou para servir arquivos estáticos em um diretório.
python -m http.server 8000
Isso sobe um servidor HTTP na porta 8000, servindo os arquivos do diretório atual.
Limitações
- Não é seguro para produção
- Sem suporte a HTTPS, autenticação, rotas ou lógica de backend
- Apenas para uso local ou ambientes controlados
Threads
Threads (ou "linhas de execução") são formas de executar múltiplas tarefas ao mesmo tempo dentro do mesmo processo. Em Python, threads são muito úteis para operações concorrentes, como:
- Fazer múltiplas requisições HTTP em paralelo
- Esperar por entradas (como leitura de arquivos ou de rede) sem travar a execução principal
- Executar tarefas em segundo plano enquanto o programa continua rodando
O Python oferece o módulo threading
, que permite criar e controlar threads manualmente. Por exemplo:
import threading
def tarefa():
print("Executando tarefa...")
# Cria uma thread
t = threading.Thread(target=tarefa)
# Inicia a thread
t.start()
# Espera a thread terminar
t.join()
Esse código roda a função tarefa
em uma nova thread, enquanto o código principal continua sua execução.
GIL (Global Interpreter Lock)
O Python possui uma trava global chamada GIL que impede múltiplas threads de executarem bytecode Python ao mesmo tempo. Isso limita o ganho de desempenho em tarefas CPU-intensivas.
Mas isso *não é um problema para tarefas I/O-intensivas, como:
- Acessar a internet
- Ler arquivos
- Esperar por sockets
Para tarefas de CPU pesado, usa-se multiprocessing (em vez de threading
).
Várias requisições em paralelo
import threading
import requests
def baixar(url):
r = requests.get(url)
print(f'{url} - {len(r.text)} bytes')
urls = [
'https://example.com',
'https://httpbin.org/delay/2',
'https://www.python.org'
]
threads = []
for url in urls:
t = threading.Thread(target=baixar, args=(url,))
t.start()
threads.append(t)
# Aguarda todas as threads terminarem
for t in threads:
t.join()
print("Todas as requisições concluídas.")
Esse script faz três downloads ao mesmo tempo, sem esperar um terminar para começar o outro.
Threads com classes
Você pode criar uma thread definindo sua própria classe:
class MinhaThread(threading.Thread):
def run(self):
print("Executando dentro da thread personalizada")
t = MinhaThread()
t.start()
t.join()