Hobby Blog About Python

HobbyPython.com

October 1, 2024

Is Python Black a linter?

Clear code is essential for maintenance. While small programs are relatively simple, larger projects can become increasingly complex. Without proper formatting, code can become increasingly difficult to understand over time. Python Black is a tool that helps enforce a consistent style of code, making it easier to read and maintain.

Key Takeaways

  • Clear and Consistent Code is Essential: Proper formatting improves code readability and maintainability, especially for larger projects.
  • Black: A Code Formatter, Not a Linter: Black enforces consistent style based on PEP 8 guidelines but doesn’t identify errors or bugs. Linters do that.
  • Black enforces its Own Style: While PEP 8 compliant, Black has its own formatting preferences you surrender control over.
  • Black in CI/CD Pipelines: You can use Black’s –check option to enforce formatting as a quality gate before builds or deployments.


Interested in learning Python? Read about: Keep Your Python Code Clean with CI/CD Linters

What is Python Black?

Black is a code formatter. A code formatter is a tool that automatically rearranges code to conform to a specific style guide. It can help improve code readability, consistency, and maintainability by enforcing consistent formatting rules. Black is a PEP 8 compliant opinionated formatter with its own style. As you can read in Black - read the docs “By using Black, you agree to cede control over minutiae of hand-formatting. In return, Black gives you speed, determinism, and freedom from pycodestyle nagging about formatting. You will save time and mental energy for more important matters”.

Is Python Black a linter?

Coming back to our main question, is Black a linter? Well, the answer is NO. Linters are static code analysis tools that automatically checks source code for potential errors, stylistic problems, and programming bugs. Linters can write cleaner, more reliable, and maintainable code by identifying and flagging issues early in the development process.

Using Black for experiments with Python

As a Python hobbyist, it’s high time to have some fun and check out Black in action. For this fun I’ll use the code I presented in the post about converting images to Webp format (Convert Your Images To WebP with Python and Speed Up Your Website). The original code looks like this:

from pathlib import Path
from PIL import Image

def convert_to_webp(source):
    
    destination = source.with_suffix(".webp")

    image = Image.open(source)  # Open image
    image.save(destination, format="webp")  # Convert image to webp

    return destination

def main():
    path_input = input("Provide source path:")
    paths = Path(path_input).glob("**/*.png")
    for path in paths:
        webp_path = convert_to_webp(path)
        print(webp_path)
    return

if __name__ == '__main__':
    main()
    

We’ll change it up a bit to give Black some work:

from pathlib import Path


from PIL import Image

def convert_to_webp(source):
    
    destination = source.with_suffix(".webp")

    image = Image.open(source)  # Open image
    image.save(destination, 
               format="webp")  # Convert image to webp

    return destination

def main():
    path_input = input('Provide source path:')
    paths = Path(path_input).glob("**/*.png")
    for path in paths:
        webp_path = 
        convert_to_webp(path)
        print(
            webp_path)
    return

if __name__ == '__main__':
    main()
    

After using black web.py, black couldn’t handle changing two lines:

webp_path =
convert_to_webp(path)
    

but after changing it to one line, as it was originally, Black reformatted the code to this:


from pathlib import Path


from PIL import Image


def convert_to_webp(source):

    destination = source.with_suffix(".webp")

    image = Image.open(source)  # Open image
    image.save(destination, format="webp")  # Convert image to webp

    return destination


def main():
    path_input = input("Provide source path:")
    paths = Path(path_input).glob("**/*.png")
    for path in paths:
        webp_path = convert_to_webp(path)
        print(webp_path)
    return


if __name__ == "__main__":
    main()

We can see that Black formatter works properly and the code looks visually better.

Does it make sense to use Black in a CI/CD pipeline?

Why not? Black can be used as a quality gate in the build process to ensure proper formatting quality – if we really care about it because of the possible increase in the number of failed builds which will certainly increase the cost of building (for credit plans). For this purpose we can use the --check option in Black. Let’s test it!

Let’s check with this simple example.py code:


def main():
    for i in range(1, 11):
        print(i)
    return
        
        
if __name__ == '__main__':
    main()
    

And just make a small change:


def main():
    for i in range(1, 11):
            print(i)
    return
        
        
if __name__ == '__main__':
    main()
    

After testing with black --check example.py the output is:


would reformat example.py

Oh no! 💥 💔 💥
1 file would be reformatted.
    

Let’s check the behaviour in the CI/CD pipeline using Travis CI. First let’s prepare our .travis.yml and test the usage of Black in our pipeline:


language: python
python:
  - "3.8"

# Install dependencies
install:
  - pip install black

jobs:
 include:
  - stage: formatter #this is our quality gate
    script: black --check example.py

# Run the script
  - stage: display
    script:
    - python example.py

And it works, here is our build log:

Lets create a quality gate with Black formatter, here is the .travis.yml:


language: python
python:
  - "3.8"

# Install dependencies
install:
  - pip install black

# Run the script
script:
  - python -m black example.py
  - python example.py

Here is what we see on our CI/CD tool Travis CI:

It works! In the first stage: “formatter” we create a quality gate, if the code is not formatted by Black the stage fails and the next stages will not be executed. This creates a great quality gate to avoid building (and maybe deploying) until the code will be formatted.

Conclusion

Python Black is a valuable tool for maintaining consistent code formatting in Python projects. While it’s not a linter, it can significantly improve code readability and maintainability by enforcing a specific style guide. By using Black, developers can save time and mental energy on formatting, allowing them to focus on more important aspects of their code. Additionally, integrating Black into CI/CD pipelines can act as a quality gate, ensuring that code meets formatting standards before being built or deployed. However, it’s important to note that Black has its own preferences and may not be able to handle all formatting scenarios automatically.

Frequently Asked Questions

Q: What is the primary purpose of Python Black?

Python Black is a code formatter that automatically rearranges Python code to conform to a specific style guide. It helps improve code readability, consistency, and maintainability by enforcing consistent formatting rules.

Q: How does Python Black differ from a linter?

While both Black and linters are used to improve code quality, they serve different purposes. Black focuses on formatting, ensuring code is consistently styled. Linters, on the other hand, analyze code for potential errors, stylistic problems, and programming bugs.

Q: Can Python Black be used as a quality gate in a CI/CD pipeline?

Yes, Python Black can be used as a quality gate in a CI/CD pipeline. By using the --check option, you can configure the pipeline to fail if the code doesn’t adhere to Black’s formatting rules. This helps ensure that only well-formatted code is merged into the main branch.

Q: Are there any limitations to Python Black’s formatting capabilities?

While Python Black is a powerful tool, it may not be able to handle all formatting scenarios automatically. In some cases, manual adjustments might be necessary to achieve the desired formatting. Additionally, Black’s style preferences may not align perfectly with everyone’s personal preferences.