Skip to main content

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)

  1. Ative a verificação em duas etapas na sua conta Gmail.

  2. 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)

  1. Porta 587 aberta (para STARTTLS) ou porta 465 (para SMTPS).
  2. Suporte a autenticação SASL (como LOGIN ou PLAIN).
  3. Um certificado TLS válido (ou aceitar conexões STARTTLS com auto-assinados, mas é menos recomendado).
  4. 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.


  1. O servidor SMTP local deve estar configurado para aceitar conexões locais (localhost/127.0.0.1).
  2. Não deve exigir autenticação para conexões locais (SASL desabilitado para 127.0.0.1).
  3. 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()