pyreorder
AST-based Python module reorganizer.
preorder reorders the top-level statements of a Python module into a canonical
section layout (imports → globals → constants → classes → functions → main)
and reorders the methods inside each class by visibility and type. It is built
on libcst, so comments and formatting
are preserved.
It is a module reorganizer: it focuses on grouping and ordering imports/globals/constants/classes/methods/etc. It does not integrate with other sorting tools (isort, undersort, etc.) or provide ruff subcommands.
Install
uv tool install pyreorder
preorder --version
Quick start
preorder run src/ # sort files in place
preorder check src/ # exit 1 if anything would change (CI / pre-commit)
preorder diff src/ # preview changes
preorder config generate # write/print a preorder.toml template (--with-comments, --with-config)
Before → after
Given this module:
import sys
def main():
greet()
def greet():
print("hi")
class Service:
def _close(self):
...
def start(self):
...
MAX_CONN = 10
if __name__ == "__main__":
main()
preorder run (with functions = "stepdown") produces:
import sys
MAX_CONN = 10
class Service:
def start(self):
...
def _close(self): # public methods first, then protected
def main(): # caller before callee (step-down rule)
greet()
def greet():
print("hi")
if __name__ == "__main__":
main()
Configuration
Discovered from (first wins, walking up from the target file): --config,
preorder.toml, .config/preorder.toml, [tool.preorder] in pyproject.toml.
[tool.preorder.module]
sections = [
"imports", "typing_imports", "module_constants", "enums",
"dataclasses", "classes", "functions", "dunder_exports", "main_block",
]
[tool.preorder.strategy] # per-section; omit => "keep"
enums = "alpha"
functions = "stepdown" # "alpha" | "stepdown" | "abstraction" | "keep"
[tool.preorder.class_methods] # undersort-style ordering within each class
enabled = true
order = ["public", "protected", "private"]
method_type_order = ["instance", "class", "static"]
Strategies
| value | meaning | applies to |
|---|---|---|
keep |
preserve original order (default) | any section |
alpha |
alphabetical by primary name | imports, enums |
stepdown |
caller before callee (top-down narrative) | functions, classes |
abstraction |
callee before caller (low-level utilities first) | functions, classes |
Caution:
alphaonmodule_constants/classes/dataclassescan break runtime order (interdependent constants, inheritance). Always preview withpreorder difffirst.
Safety model
preorder is conservative by design:
- Barriers — statements that don't map to a configured section (runtime
setup like
app = typer.Typer()) are never moved. Recognised statements only reorder within their contiguous barrier-free run, so preorder never moves code across a statement it might depend on. - Pinned — the module docstring and
from __future__ import ...always stay first. - Opt-out — a
# preorder: off(or# nosort) comment in a file's header skips the file;class C: # preorder: offskips that class. - Idempotent — running
preordertwice never changes a file a second time.
Programmatic API
from pyreorder import sort_source, Config
cfg = Config(strategies={"functions": "stepdown"})
sorted_text = sort_source(source_text, cfg)
Pre-commit
repos:
- repo: https://github.com/jr2804/pyreorder
rev: v0.1.0
hooks:
- id: preorder
Agent skill
An installable agent skill lives in skills/pyreorder.
Install it for your AI assistant:
bun x skills add https://github.com/jr2804/pyreorder.git -s pyreorder -a universal -y
Development
uv sync --dev # install dev dependencies
uv run pytest # tests
uvx ruff check . # lint
uvx ruff format . # format
Acknowledgements
The in-class method sorter is an adapted reimplementation of undersort (MIT). Dependency-aware function ordering was inspired by ssort, sdsort and ABSort. See Credits.
License
MIT — see LICENSE.
Metadata
Release files for pyreorder 2026.9.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyreorder-2026.9.1.tar.gz | 115.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyreorder-2026.9.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 157.2 kB
Release files / pyreorder-2026.9.1.tar.gz
| Download URL | pyreorder-2026.9.1.tar.gz |
|---|---|
| Size | 115.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bfb6cc694c82207d64c1b5887d744e9392c8a662cfced4adcb11ce5894d6cc99
|
|
BLAKE2b-256 checksum How to use checksums |
2260e27cf34c23b3f7ace80d761e35afe0c054af2d4933f41a9e7e3b2f020afc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logRelease files / pyreorder-2026.9.1-py3-none-any.whl
| Download URL | pyreorder-2026.9.1-py3-none-any.whl |
|---|---|
| Size | 41.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6bf9a69f22dab5e5341aa1c573932da8d9e56e4edf7d2d2dedc8e3209f3a96b0
|
|
BLAKE2b-256 checksum How to use checksums |
b861e0ab6e9138e617b694b20aef78dd7a2e0904b1e214935e5bd3640142c7e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency log