Automatically add trailing commas to calls and literals in Python code
npx @tessl/cli install tessl/pypi-add-trailing-comma@3.2.0A Python tool and pre-commit hook that automatically adds trailing commas to function calls, literals (lists, tuples, dicts, sets), function definitions, import statements, class definitions, with statements, match statements, and PEP-695 type aliases. This improves code maintainability by ensuring consistent trailing comma usage, reducing git diff noise, preserving git blame information, and avoiding arbitrary indentation issues.
pip install add-trailing-commaThis package is primarily designed for command-line usage and does not export a public programmatic API. The __init__.py is empty, making direct imports unsuitable for typical programmatic use.
Command-line usage (recommended):
add-trailing-commaPython module invocation:
python -m add_trailing_commaInternal API access (for advanced use cases, accessing private modules):
from add_trailing_comma._main import main, fix_file# Process single file
add-trailing-comma script.py
# Process multiple files
add-trailing-comma file1.py file2.py src/
# Read from stdin
cat script.py | add-trailing-comma -
# Exit with 0 even if files were changed (useful for CI)
add-trailing-comma --exit-zero-even-if-changed file.pyAdd to your .pre-commit-config.yaml:
- repo: https://github.com/asottile/add-trailing-comma
rev: v3.2.0
hooks:
- id: add-trailing-commaNote: The following shows internal API usage accessing private modules. This is not recommended for production use as private APIs may change without notice.
import argparse
from add_trailing_comma._main import fix_file, main
# Process a single file using internal API
args = argparse.Namespace(exit_zero_even_if_changed=False)
result = fix_file("script.py", args)
# Use main function with arguments
result = main(["script.py", "--exit-zero-even-if-changed"])The main entry point for command-line usage with argument parsing and file processing.
def main(argv: Sequence[str] | None = None) -> int:
"""
Main entry point for command line interface.
Parameters:
- argv: Command line arguments (defaults to sys.argv if None)
Returns:
int: Exit code (0 for success or no changes, 1 for errors or changes made)
Available command line options:
- filenames: List of files to process
- --exit-zero-even-if-changed: Exit with 0 even if files were modified
- --py35-plus, --py36-plus: Legacy flags (deprecated, no effect)
"""Process individual files to add trailing commas with configurable options.
def fix_file(filename: str, args: argparse.Namespace) -> int:
"""
Process a single file to add trailing commas.
Parameters:
- filename: Path to file to process, or '-' for stdin
- args: Namespace with configuration options (requires exit_zero_even_if_changed attribute)
Returns:
int: 1 if file was changed or error occurred, 0 if no changes or exit_zero_even_if_changed=True
Handles:
- Unicode decode errors (prints error message, returns 1)
- Syntax errors (returns content unchanged)
- File I/O operations for both files and stdin
"""Core internal functionality for transforming Python source code to add trailing commas. This is a private function used internally by the tool.
def _fix_src(contents_text: str) -> str:
"""
Internal function that applies AST transformations to add trailing commas.
Note: This is a private function (prefix '_') and should not be used in
production code as it may change without notice.
Parameters:
- contents_text: Python source code as string
Returns:
str: Transformed source code with trailing commas added
Raises:
SyntaxError: Returns original content if syntax errors are encountered
"""Adds trailing commas to multi-line function calls:
# Before
function_call(
argument,
5 ** 5,
kwarg=foo
)
# After
function_call(
argument,
5 ** 5,
kwarg=foo,
)Adds trailing commas to lists, tuples, dictionaries, and sets:
# Lists
x = [
1, 2, 3, # trailing comma added
]
# Tuples
y = (
'a', 'b', 'c', # trailing comma added
)
# Dictionaries
config = {
'key1': 'value1',
'key2': 'value2', # trailing comma added
}
# Sets
items = {
'item1',
'item2', # trailing comma added
}Adds trailing commas to function and method parameters:
def func(
arg1,
arg2, # trailing comma added
):
pass
async def async_func(
arg1,
arg2, # trailing comma added
):
passAdds trailing commas to from-import statements:
from os import (
path,
makedirs, # trailing comma added
)Adds trailing commas to class inheritance lists:
class MyClass(
Base1,
Base2, # trailing comma added
):
passAdds trailing commas to with statement context managers:
with (
open('f1', 'r') as f1,
open('f2', 'w') as f2, # trailing comma added
):
passAdds trailing commas to match patterns:
match x:
case A(
1,
2, # trailing comma added
):
pass
case (
1,
2, # trailing comma added
):
pass
case [
1,
2, # trailing comma added
]:
pass
case {
'x': 1,
'y': 2, # trailing comma added
}:
passAdds trailing commas to type parameter lists:
def f[
T, # trailing comma added
](x: T) -> T:
return x
class A[
K, # trailing comma added
]:
def __init__(self, x: K) -> None:
self.x = x
type ListOrSet[
T, # trailing comma added
] = list[T] | set[T]The tool also performs "unhugging" - formatting parentheses positioning:
# Unhug trailing paren
x(
arg1,
arg2, # trailing comma and proper closing
)
# Unhug leading paren
function_name(
arg1,
arg2,
)
# Match closing brace indentation
x = [
1,
2,
3,
] # proper indentationRemoves unnecessary commas from single-line structures:
# Before
[1, 2, 3,]
[1, 2, 3, ]
# After
[1, 2, 3]
[1, 2, 3]The tool supports the following command line arguments:
The tool handles various error conditions gracefully:
The tool is designed to work seamlessly with pre-commit hooks for automated code formatting in development workflows.
Use --exit-zero-even-if-changed flag in CI environments where you want the tool to run but not fail the build when changes are made.
The tool relies on the following dependencies and type definitions:
# Required external dependency
from tokenize_rt import src_to_tokens, tokens_to_src, Token, Offset
# Standard library dependencies
from typing import Sequence, Iterable
from collections.abc import Sequence
from argparse import Namespace, ArgumentParser
import ast
import sys# Main API functions
def main(argv: Sequence[str] | None = None) -> int
def fix_file(filename: str, args: Namespace) -> int
# Internal transformation function
def _fix_src(contents_text: str) -> str
# Helper types from tokenize-rt
Token: # Represents a token with .src, .name, .offset attributes
Offset: # Represents position in source (line, column)