Skip to main content

Como melhorar a qualidade do código com linting em Python

Escrito por

Keshav Malik

feature python linting

12 de outubro de 2022

0 minutos de leitura

Python é uma linguagem em constante crescimento. À medida que ela evolui e se expande, também aumenta o número de ferramentas e estratégias de desenvolvimento disponíveis para trabalhar com ela. Um processo que vem ganhando popularidade é o linting, ou seja, a verificação do código em busca de possíveis problemas. Com o linting, os erros no código são sinalizados para que possamos corrigir práticas de programação incomuns que poderiam causar problemas.

O linting é feito enquanto o código-fonte está sendo escrito, antes de ele ser compilado. Em outras palavras, é uma verificação pré-build, também chamada de “análise estática de código”. Verificar o código regularmente com linting garante consistência em todo o código e na base de código. Isso reduz as chances de pequenos erros se transformarem em problemas complexos depois que o código é executado.

Muitos desenvolvedores não usam linting porque não percebem o valor que ele agrega, já que não impede bugs. Mas essa visão subestima o quanto o linting pode melhorar a qualidade do código.

Neste artigo prático, vamos explorar como é rápido e fácil fazer verificações de linting em Python com o Pylint, uma das ferramentas de linting mais populares. Também veremos como o linting pode ajudar você a seguir o guia de estilo de código PEP8.

Pré-requisitos

Antes de começar, confira se você tem:

  • Python e pip instalados na sua máquina

  • Conhecimentos básicos sobre interfaces de linha de comando (CLIs)

  • Conhecimento de conceitos de Python, como funções e classes

Observe também que, embora os comandos apresentados aqui sejam compatíveis com sistemas Linux e macOS, é preciso ter cuidado ao trabalhar com Windows.

Fazendo linting de código Python

Antes de mostrar como usar um linter em Python, vamos preparar o ambiente criando um diretório e um ambiente virtual.

Configurando o ambiente

Primeiro, crie um diretório para o projeto. Neste tutorial, vamos chamá-lo de pylint-demo.

$ mkdir pylint-demo

$ cd pylint-demo

Em seguida, crie um ambiente virtual. Ele vai isolar as dependências do projeto e evitar conflitos com outros projetos.

$ pip install pipenv

$ pipenv shell

O prompt deve ficar parecido com: (pylint-demo) $. Isso indica que o ambiente virtual está ativo.

Com o ambiente virtual ativo, instale o linter com o comando a seguir:

$ pipenv install pylint

Agora podemos executar o linter com o comando pylint. Para confirmar que o Pylint foi instalado corretamente, execute o seguinte comando:

$ pylint –help

Primeiros passos com linting

Vamos escrever um programa Python básico e usar o Pylint nele para ver como funciona. Crie um arquivo main.py e copie o código a seguir:

def is_number_even(num):
    return "Even" if num % 2 == 0 else "Odd"

num = 5
print(f"The number {num} is {is_number_even(num)}")

No código acima, adicionamos uma função para verificar se um número é par ou ímpar. Para usar o Pylint e verificar se há erros nesse código, executamos o seguinte comando:

$ pylint <<file_name>>
$ pylint main.py

A saída do Pylint é a seguinte:

************* Module main
main.py:12:0: C0304: Final newline missing (missing-final-newline)
main.py:1:0: C0114: Missing module docstring (missing-module-docstring)
main.py:8:0: C0116: Missing function or method docstring (missing-function-docstring)
main.py:8:19: W0621: Redefining name 'num' from outer scope (line 11) (redefined-outer-name)
main.py:11:0: C0103: Constant name "num" doesn't conform to UPPER_CASE naming style (invalid-name)

------------------------------------------------------------------
Your code has been rated at 0.00/10 (previous run: 0.00/10, +0.00)

Podemos identificar vários problemas autoexplicativos no código, cada um identificado por um código, como C0304. O Pylint atribui uma letra a cada erro para diferenciar a gravidade e a natureza do problema. Há cinco categorias de erros:

  • C: Convenção (para violações de convenções de código)

  • R: Refatoração (para problemas relacionados a code smells e refatoração)

  • W: Aviso (para problemas de programação que não são erros)

  • E: Erro (para problemas de programação que são erros)

  • F: Fatal (para problemas graves que interromperam a execução do Pylint)

O Pylint também atribui ao código uma nota de 0 a 10, com base no número de erros encontrados.

No nosso exemplo, todos os códigos de erro, exceto um, indicam problemas de convenção; o único restante é um aviso. Para corrigir esses problemas, vamos fazer algumas alterações no código e executar o Pylint novamente para ver que nota ele recebe.

""" File contains various function to under Pylint """

def is_number_even(num):
    """Function to check if number is even or odd"""
    return "Even" if num % 2 == 0 else "Odd"

NUM = 5
print(f"The number {NUM} is {is_number_even(NUM)}")

Nesse código, adicionamos docstrings ao módulo e à função, inserimos uma nova linha no final e renomeamos a variável do código anterior. Ao executar o Pylint novamente, recebemos nota 10/10, sem nenhum problema.

Executando o Pylint em um único arquivo

Agora que entendemos melhor como o Pylint funciona, vamos ver outro exemplo. Digite o código a seguir:

""" File contains various function to under Pylint """

class animal:
  def __init__(self, name):
    self.name = name

obj1 = animal("Horse", 21)
print(obj1.name)

Neste trecho, temos uma classe simples chamada animal e um objeto dessa classe chamado obj1. Agora, vamos usar o Pylint nesse código.

************* Module main
main.py:4:0: W0311: Bad indentation. Found 2 spaces, expected 4 (bad-indentation)
main.py:5:0: W0311: Bad indentation. Found 4 spaces, expected 8 (bad-indentation)
main.py:3:0: C0115: Missing class docstring (missing-class-docstring)
main.py:3:0: C0103: Class name "animal" doesn't conform to PascalCase naming style (invalid-name)
main.py:3:0: R0903: Too few public methods (0/2) (too-few-public-methods)
main.py:7:7: E1121: Too many positional arguments for constructor call (too-many-function-args)

Observe que, desta vez, os problemas de qualidade do código deram lugar a erros mais substanciais. Com os problemas sinalizados, vamos tentar corrigi-los com o código abaixo:

""" File contains various function to under Pylint """

class Animal:
    "Animal Class"
    def __init__(self, name):
        self.name = name

obj1 = Animal("John")
print(obj1.name)

Em seguida, execute o Pylint novamente. Depois de mudar o nome da classe de animal para Animal, adicionar uma docstring à classe, remover argumentos desnecessários das chamadas de função e incluir a indentação correta, quase eliminamos todos os erros do código. Ainda resta um:

************* Module main
main.py:3:0: R0903: Too few public methods (0/2) (too-few-public-methods)

Vamos ver como corrigir esse erro restante. O Pylint informa que não temos dois ou mais métodos públicos, mas é bem provável que nosso código realmente não tenha dois ou mais métodos públicos. Então, como corrigimos isso?

Nesse caso, podemos usar comentários em Python para suprimir esses problemas. A sintaxe para fazer isso é:

# pylint: disable=<<issue_name>>

Veja como ficará o código:

""" File contains various function to under Pylint """

# pylint: disable=too-few-public-methods
class Animal:
    "Animal Class"
    def __init__(self, name):
        self.name = name

obj1 = Animal("John")
print(obj1.name)

Ao verificar a saída do Pylint agora, veremos que o problema desapareceu.

Executando o Pylint em um diretório

Vimos como executar o Pylint em um único arquivo, mas, ao trabalhar em um projeto, não teremos apenas um arquivo para verificar. Precisaremos fazer linting do diretório.

Para usar o Pylint em todo o diretório, execute o seguinte comando:

$ pylint <<name_of_directory>>

Para ver como funciona o linting de um diretório, vamos criar mais dois arquivos e adicionar algum código.

$ mkdir src; cd src
$ touch helpers.py config.py __init__.py

Mova o arquivo main.py para o diretório src e cole o código a seguir nos respectivos arquivos:

<<main.py>>
""" File contains various function to under Pylint """

from helpers import connect_db
from config import DB_USER, DB_PASS

is_connected = connect_db(DB_USER, DB_PASS)

if is_connected:
    print("Connected to DB")
else:
    print("Failed to connect to DB")

<<helpers.py>>
def connect_db(user, password):
    """Dummy function to connect to DB"""
    if user is None or password is None:
        return False
    return True

<<config.py>>
DB_USER = "root"
DB_PASS = "toor"

Temos três arquivos no diretório src: main.py, helpers.py e config.py. Em main.py, temos uma função fictícia que exibe se estamos conectados ao banco de dados. O arquivo helpers.py contém uma função auxiliar fictícia para conectar ao banco de dados, e o arquivo config.py contém o nome de usuário e a senha do banco de dados.

Agora, vamos executar o Pylint em todo o diretório usando o seguinte comando a partir do diretório raiz:

$ pylint src

A saída do comando será:

************* Module src.config
src/config.py:2:0: C0304: Final newline missing (missing-final-newline)
src/config.py:1:0: C0114: Missing module docstring (missing-module-docstring)
************* Module src.main
src/main.py:11:0: C0304: Final newline missing (missing-final-newline)
src/main.py:3:0: E0401: Unable to import 'helpers' (import-error)
src/main.py:4:0: E0401: Unable to import 'config' (import-error)
************* Module src.helpers
src/helpers.py:6:0: C0304: Final newline missing (missing-final-newline)
src/helpers.py:1:0: C0114: Missing module docstring (missing-module-docstring)

Como podemos ver, o Pylint exibe os resultados de arquivos diferentes, separados por *** e pelo nome do módulo. Para corrigir os problemas, precisamos fazer as seguintes alterações:

  • Adicione uma nova linha no final de cada arquivo.

  • Adicione uma docstring a cada arquivo e função.

  • Altere a instrução import de helpers import connect_db para .helpers import connect_db.

Depois de corrigir esses problemas, encontraremos outro: precisamos colocar em maiúscula o nome da variável is_connected. Podemos mudar o nome da variável ou suprimir o aviso para lidar com esse erro.

Suprimindo avisos

É bem provável que você precise personalizar ou suprimir vários avisos ao fazer linting do seu código Python. Não faz sentido adicionar um comentário a cada vez. Em vez de suprimir os avisos um por um, você pode criar um arquivo .rc para personalizar o comportamento do Pylint e suprimir os avisos do projeto inteiro diretamente nesse arquivo .rc.

Você pode criar um arquivo desse tipo com o seguinte comando:

$ pylint --generate-rcfile > pylint.rc

Código melhor e mais seguro com linting

O linting em Python verifica o código-fonte enquanto ele é escrito e sinaliza erros, antes da execução. Você também pode integrar o Pylint aos editores para ver os resultados do linting em tempo real.

Embora o linting não corrija bugs automaticamente, usá-lo de forma consistente ajuda a manter a alta qualidade do código. Por isso, mesmo que alguns desenvolvedores considerem o linting uma perda de tempo, ele é muito eficaz para detectar pequenos problemas antes que se transformem em problemas maiores.

Ao longo deste artigo, vimos como o linting e a aplicação das recomendações do Pylint melhoraram nosso código de exemplo. Além disso, esse processo ajuda a seguir o guia de estilo PEP8. Agora que você sabe como implementar o linting nos seus projetos, pode explorar as diversas ferramentas disponíveis e descobrir qual delas complementa melhor — e aprimora — sua abordagem ao desenvolvimento em Python.

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.

Leia mais

feature insights context
Blog

Os ataques autônomos já chegaram. A defesa precisa acompanhar o ritmo.

Os atacantes autônomos estão reduzindo o tempo disponível para a defesa. Saiba como a descoberta, a correção, a validação e a prevenção contínuas ajudam as equipes de segurança a acompanhar esse ritmo.

illustration hero ai
Blog

O furacão da IA chegou

A IA está acelerando tanto a criação de software quanto os ataques cibernéticos. As lideranças devem proteger agentes e código desde o início, aplicar controles em tempo de execução e validar as defesas de forma independente.

feature insights context
Blog

A prevenção é essencialmente um problema resolvido?

A prevenção em código gerado por agentes está resolvida do ponto de vista arquitetural — mas escolher controles que protejam a segurança sem desacelerar o desenvolvimento continua sendo um desafio.