Ruffing Up Python Code: A Stylish Makeover for your Code

by Kushal Gajurel, Founder / CTO

In the world of Python programming, maintaining clean, readable, and error-free code is paramount. Whether you're working on a personal project or collaborating within a team, adhering to consistent coding standards not only enhances the readability of your code but also improves its maintainability and reliability. This is where linting and formatting tools come into play.

Linting tools analyze your code for potential errors, style violations, and other issues that might lead to bugs or inconsistencies. They help enforce coding standards and best practices, ensuring that your codebase remains robust and error-free. On the other hand, formatting tools focus on the visual appearance of your code, automatically adjusting indentation, spacing, and other stylistic elements to maintain a uniform and professional look across your project.

Ruff: Your new best friend for fast code linting

Many Python developers are familiar with and commonly use Flake8 as their default linter. I myself have extensively utilized this exceptional library. However, my recent discovery of Ruff suggests it may revolutionize the landscape. In this article, I'll delve into some of its notable features.

Installation

To install Ruff, you have two options: either using the Python package manager or globally. Ensure you have Python 3.7 or higher installed, along with package managers such as pip, conda, or poetry for the Python-style installation.

$ pip install ruff
# or
$ poetry add ruff -G dev
# or
$ conda install -c conda-forge ruff

Alternatively, you have the option to install Ruff globally using tools like pipx, Homebrew, or your default operating system package manager in certain cases.

$ pipx install ruff

# macOS
$ brew install ruff

# Arch Linux
$ pacman -S ruff

# Alpine
$ apk add ruff

Usage

Let's examine the following code snippet:

from typing import List

import os

def sum_even_numbers(numbers: List[int]) -> int:
    """Given a list of integers, return the sum of all even numbers in the list."""
    return sum(num for num in numbers if num % 2 == 0)

To analyze this code with Ruff, execute the following command:

$ ruff check .

The dot at the end signifies the current working directory, prompting Ruff to scan all Python files within this directory and its subdirectories. Typically, you would execute this command in the project's root directory. Ruff will display any encountered errors during the parsing of the source code. It's noteworthy that Ruff employs the same error codes as Flake8, ensuring consistency and familiarity.

If you wish to rectify the code, akin to autoflake's functionality, append the fix option to your Ruff command:

$ ruff check --fix .

Configuration

To configure Ruff, you'll utilize either a ruff.toml file or a pyproject.toml file. All settings in this file will fall under the [tool.ruff] section. By default, Ruff supports pyflakes and pycodestyle rules, which form the basis of Flake8. However, you have the flexibility to configure additional rules from over 40 plugins, including pyupgrade and isort (yes, you can even sort imports with Ruff!).

[tool.ruff]
line-length = 100  # defaults to 88 like black
target-version = "py39"  # the Python version to target, useful when considering code upgrades, defaults to "py310"

select = [
  "E",   # pycodestyle
  "F",   # pyflakes
  "UP",  # pyupgrade,
  "I",   # isort
]

# If you want to configure a specific plugin, you can do so in a subsection, typically following the plugin's supported configuration.
[tool.ruff.isort]
...

Disregarding Errors

Although it is not a good practice to disregard the error, Ruff does have an option to do that.

Within the Code Itself

Errors can be disregarded at either a line-by-line level or for the entire file. To ignore errors on a line level, you can use a # noqa: UP006 comment directly in the code:

from typing import List

def sum_even_numbers(numbers: List[int]) -> int:  # noqa: UP006
    """Given a list of integers, return the sum of all even numbers in the list."""
    return sum(num for num in numbers if num % 2 == 0)

Alternatively, to ignore errors for the entire file, place the # ruff: noqa: UP006 statement at the top:

# ruff: noqa: UP006
from typing import List

def sum_even_numbers(numbers: List[int]) -> int:
    """Given a list of integers, return the sum of all even numbers in the list."""
    return sum(num for num in numbers if num % 2 == 0)

In the Configuration File

In the configuration file, ignore and per-file-ignores options can be utilized for this purpose. Additionally, the unfixable option for the bugbear plugin ensures that errors reported by this plugin are not attempted to be fixed:

# Enable flake8-bugbear (`B`) rules.
select = ["E", "F", "B"]

# Never enforce `E501` (line length violations).
ignore = ["E501"]

# Avoid attempting to fix flake8-bugbear (`B`) violations.
unfixable = ["B"]

# Ignore `E402` (import violations) in all `__init__.py` files, and in `path/to/file.py`.
[per-file-ignores]
"__init__.py" = ["E402"]
"path/to/file.py" = ["E402"]

On the Command Line

To execute Ruff from the command line and ignore E402 errors:

$ ruff check path/to/code/ --ignore E402

Continuous integration / Continuous deployment

To help with your workflow, Ruff comes with a pre-commit hook you can use.

- repo: https://github.com/charliermarsh/ruff-pre-commit
  # Ruff version.
  rev: 'v0.0.257'
  hooks:
    - id: ruff

If you want to enable auto-fix, you can complete the previous configuration like this:

- repo: https://github.com/charliermarsh/ruff-pre-commit
  # Ruff version.
  rev: 'v0.0.258'
  hooks:
    - id: ruff
      args: [--fix, --exit-non-zero-on-fix]

It is worth noting that you can format how the output will appear in your favorite CI/CD platform with the format configuration option.

[tool.ruff]
format = "gitlab"

Python CI

Consistent code formatting is key to readability and collaboration. Let's ensure all our projects follow the same standards by setting up linting with GitHub Actions or Gitlab CI/CD pipelines. If your project doesn’t have the Continuous Integration action for linter make sure to introduce it for each Pull Request. If it's not already present in the repo, Create one.

Create a file (.github/workflows/lint.yml) inside your repository with the following content

Copy code
name: Linter
on: [push, pull_request]
jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: chartboost/ruff-action@v1

Conclusion

In conclusion, maintaining clean, readable, and error-free code is essential in Python programming, whether you're working individually or within a team. Linting and formatting tools play a crucial role in achieving this goal by analyzing code for errors, enforcing coding standards, and improving its visual appearance.

While Flake8 has been a popular choice among Python developers, the emergence of Ruff offers a modern alternative with enhanced functionality and performance optimizations. With its robust linting and formatting capabilities, Ruff aims to streamline the development process and elevate code quality.

This documentation has provided insights into installing, configuring, and effectively using Ruff for Python linting and formatting. By integrating Ruff into your workflow, you can identify and rectify issues in your code, maintain consistent coding styles, and ultimately enhance the quality of your Python projects. Embrace Ruff as your new best friend for fast and efficient code linting, and experience the benefits it brings to your development journey.

More articles

A Yogi`s Guide to Debug Python Programs

Debugging code is a crucial skill for any programmer. While there are numerous resources on writing code, there's a scarcity of guidance on debugging techniques. In this article, I'll outline various approaches to debug both synchronous and asynchronous Python programs.

Read more

Impact of Evolving Artificial Intelligence on the Human Race

The evolving impact of Artificial Intelligence (AI) on humanity, from its historical origins to its integration into daily life. Drawing on research and industry insights, the narrative explores perceptions, advancements, and potential threats, advocating for a balanced approach to AI development.

Read more

Tell us about your project

Our offices

  • Dublin
    Lucan
    Dublin 15, Ireland
    Phone: +353 896009961
  • Oslo
    Borggata
    0650 Oslo, Norway
    Phone: +47 93948954
  • Dallas
    DFW
    Texas, USA
    Phone: +1 4695627406
  • Bhaktapur
    Balkot
    44800, Bagmati, Nepal
    Phone: +9779843000399