A Python source code ordering linter.
pyorder is a development tool built on the ast library for ordering Python
source code according to specified rules and conventions. Specifically, it is
designed to order definitions within a Python module. It does not attempt to order
everything in a source file, like attributes or statements outside of definitions.
pyorder focuses on ordering Python code according to best practices regarding
readability of Python code.
Requires Python 3.12+.
# install pyorder as a development dependency
uv add pyorder --dev
# check Python files
uv run pyorder check path/to/your/file1.py path/to/your/file2.py
# fix Python files
uv run pyorder check --fix path/to/your/file1.py path/to/your/file2.pypyorder check will only exit with a zero status code if all files were found
to be correctly ordered from the beginning, anything else will cause exit code 1,
even if the files are later fixed with --fix.
The --fix option will modify all files that are not ordered according to the rules.
Although pyorder attempts to respect valid Python syntax, it does NOT guarantee
valid Python syntax or that no logical errors will be introduced after reordering.
It simply reorders the definitions according to the rules. Run pyorder without
the --fix option first to see the diff output of files that would be modified.
Please check all modified files after fixing to ensure that your code is correct.
As can be seen from this design, pyorder can be very nicely integrated as a pre-commit
hook.
pyorder parses a given file as an abstract syntax tree (AST) and considers instances
of ast.FunctionDef, ast.AsyncFunctionDef, and ast.ClassDef on the module level
and the class level within module-level classes. The philosophy behind this being
that definitions structure a codebase and ordering just those gives a good balance
between a forced order for readability and structure and freedom to write your
code as you please.
pyorder enforces the rules on two levels, module-level definitions and
class-level definitions of module-level classes.
The listed rules below are applied as a sorting key in that priority order within
each level of definitions.
- callers-before-callees: a definition that uses another definition should be defined before it. (Hard dependencies that are needed at definition time are ordered before it, e.g. base classes, arg/kwargs defaults, or decorators).
- functions-before-classes: functions should be defined before classes within the module.
- visibility-ordering: public before private definitions (e.g.,
public,_private,__more_private) - alphabetical-ordering: definitions are ordered alphabetically.
- classes-before-methods: nested classes should be defined before methods.
- constructor-first: Constructor methods(e.g.,
__init_subclass__,__new__,__init__) should be defined before other methods. - abstract-first: methods decorated with
@abstractmethodshould be defined before other methods. - overrides-first: methods decorated with
@overrideshould be defined before other methods. - special-methods-first: special methods (e.g.,
__str__,__repr__) should be defined before other methods. - classmethods-first: methods decorated with
@classmethodshould be defined before other methods. - callers-before-callees: a definition that uses another definition should be defined before it. (Hard dependencies that are needed at definition time are ordered before it, e.g. base classes, arg/kwargs defaults, or decorators).
- visibility-ordering: public before private definitions (e.g.,
public,_private,__more_private) within the class. - alphabetical-ordering: definitions are ordered alphabetically within the class.
Before:
class ExampleClass:
def public_method(self):
return self._private_method()
def _private_method(self):
return "done"
class NestedExample:
pass
def __init__(self):
pass
def _private_helper():
return ExampleClass()
def public_caller():
return _private_helper()After running pyorder check --fix example.py:
def public_caller():
return _private_helper()
def _private_helper():
return ExampleClass()
class ExampleClass:
class NestedExample:
pass
def __init__(self):
pass
def public_method(self):
return self._private_method()
def _private_method(self):
return "done"pyorder does not support inline comments like # noqa. The missing support for
inline comments is intentional and is part of this tool's philosophy of enforcing
a consistent ordering of Python source code. However pyorder is built on pyrig-runtime.
Therefore any customizations and adjustments can be achieved by writing a plugin
based on the subclassing system of pyrig-runtime.
Please refer to the official documentation for more details and examples.