|
| 1 | +--- |
| 2 | +layout: "post" |
| 3 | +title: "isort + git: Cleaner Import Statements for Those Who Don't Like pre-commit" |
| 4 | +date: 2024-12-12 |
| 5 | +--- |
| 6 | + |
| 7 | +> [!NOTE] |
| 8 | +> |
| 9 | +> This is for the readers who don't want to run [`isort` as a `pre-commit` hook](https://pycqa.github.io/isort/docs/configuration/pre-commit.html), |
| 10 | +> but do want to organize their imports in one fell swoop. |
| 11 | +> |
| 12 | +> Personally, I use [`pre-commit`](https://pre-commit.com/) as it saves me from having to remember to sort my imports before submitting a PR. |
| 13 | +> But that's me. |
| 14 | +
|
| 15 | +While reviewing pull requests (PRs) at work, I sometimes notice that the imports are kind of... disorganized. |
| 16 | +```python |
| 17 | +# A made-up example. |
| 18 | + |
| 19 | +from itertools import chain |
| 20 | +from . import local_module |
| 21 | +import pandas as pd |
| 22 | +import numpy as np |
| 23 | +from typing import Generator |
| 24 | + |
| 25 | +... |
| 26 | + |
| 27 | +``` |
| 28 | +I can't fault any single author for this, it just happens over time. |
| 29 | +To help myself and future authors of the codebase, |
| 30 | +I try to make suggestions that make it easier to read or add a bit more structure. |
| 31 | + |
| 32 | +On a recent PR I made the suggestion to use [`isort`](https://pycqa.github.io/isort/) on all the modified files. |
| 33 | +Then it occurred to me that if there were more than a few modified files, this could be quite tedious! |
| 34 | +To do this more lazily—er, |
| 35 | +I mean _efficiently_!—I could take advantage of [`git`](https://git-scm.com/) to get a list of modified files |
| 36 | +and feed them to `isort` as arguments. |
| 37 | + |
| 38 | +# `git` the modified files |
| 39 | +> [!NOTE] |
| 40 | +> |
| 41 | +> If you don't already use `git` to track your files, I highly recommend starting. |
| 42 | +> It's helped me in more ways that I can mention, and I find myself learning something new more often than not. |
| 43 | +
|
| 44 | +`git` tracks changes to files and writes those as [`commit`](https://git-scm.com/docs/git-commit)s to a [`log`](https://git-scm.com/docs/git-log). |
| 45 | +Normally it's a best practice to make changes to a branch different from your primary branch |
| 46 | +(i.e. "main", "master", etc.), |
| 47 | +then open a pull request to merge those changes into the primary branch. |
| 48 | +This keeps code from being pushed to users without being reviewed or passing tests, among other things. |
| 49 | + |
| 50 | +Another benefit of tracking work on a separate branch is |
| 51 | +you can see which files have been added or modified when compared with the primary branch with [`git diff`](https://git-scm.com/docs/git-diff). |
| 52 | + |
| 53 | +Assuming we're on our feature branch and synced with the main branch, |
| 54 | +we can get the list of files like so: |
| 55 | +```shell |
| 56 | +$ git diff main --name-only |
| 57 | +_posts/2024-12-12-isort.md |
| 58 | +``` |
| 59 | +Here we can see that the only file different from my main branch is "_posts/2024-12-12-isort.md", |
| 60 | +the blog post I'm currently writing. |
| 61 | + |
| 62 | +>[!NOTE] |
| 63 | +> |
| 64 | +> We can go a bit further by limiting the file types like so: |
| 65 | +> ```shell |
| 66 | +> $ git diff main --name-only -- *.py |
| 67 | +> ``` |
| 68 | +> |
| 69 | +> This returns nothing on my machine because I haven't added/modified any git-tracked *.py files on this branch. |
| 70 | +
|
| 71 | +# `isort` the modified files |
| 72 | +Now that we have a convenient way to identify our modified files, |
| 73 | +let's save them to a variable and `isort` them. |
| 74 | +
|
| 75 | +First we save: |
| 76 | +```shell |
| 77 | +$ files=$(git diff main --name-only -- *.py) |
| 78 | +``` |
| 79 | +
|
| 80 | +Then we `isort`: |
| 81 | +```shell |
| 82 | +$ isort $files |
| 83 | +``` |
| 84 | + |
| 85 | +And that's it! |
| 86 | +Well, technically you would `git commit` your changes and `git push` them to update your PR, |
| 87 | +but you get the [`gist`](https://docs.github.com/en/get-started/writing-on-github/editing-and-sharing-content-with-gists/creating-gists). 😉 |
| 88 | + |
| 89 | +# Bonus |
| 90 | +As a final sendoff, |
| 91 | +I'll share the diff-result of applying `isort` to the example import statements above, |
| 92 | +as well as my preferred `isort` configurations à la [`.isort.cfg`](https://pycqa.github.io/isort/docs/configuration/config_files.html#isortcfg-preferred-format). |
| 93 | + |
| 94 | +Results: |
| 95 | +```shell |
| 96 | +$ isort main.py --diff |
| 97 | +``` |
| 98 | +```diff |
| 99 | + # A made-up example. |
| 100 | + |
| 101 | + from itertools import chain |
| 102 | ++ |
| 103 | ++import numpy as np |
| 104 | ++import pandas as pd |
| 105 | ++ |
| 106 | + from . import local_module |
| 107 | +-import pandas as pd |
| 108 | +-import numpy as np |
| 109 | + |
| 110 | + ... |
| 111 | + |
| 112 | +``` |
| 113 | + |
| 114 | +Config: |
| 115 | +```text |
| 116 | +[settings] |
| 117 | +profile = black |
| 118 | +line_length = 79 |
| 119 | +force_alphabetical_sort_within_sections = true |
| 120 | +force_sort_within_sections = true |
| 121 | +group_by_package = true |
| 122 | +honor_noqa = true |
| 123 | +remove_redundant_aliases = true |
| 124 | +float_to_top = true |
| 125 | +color_output = true |
| 126 | +combine_as_imports = true |
| 127 | +``` |
| 128 | + |
| 129 | +<script src="https://giscus.app/client.js" |
| 130 | + data-repo="it176131/it176131.github.io" |
| 131 | + data-repo-id="R_kgDOK1ukqg" |
| 132 | + data-category="Announcements" |
| 133 | + data-category-id="DIC_kwDOK1ukqs4CcOnS" |
| 134 | + data-mapping="pathname" |
| 135 | + data-strict="0" |
| 136 | + data-reactions-enabled="1" |
| 137 | + data-emit-metadata="0" |
| 138 | + data-input-position="top" |
| 139 | + data-theme="light" |
| 140 | + data-lang="en" |
| 141 | + data-loading="lazy" |
| 142 | + crossorigin="anonymous" |
| 143 | + async> |
| 144 | +</script> |
0 commit comments