Force relative/absolute imports
Hi !
I read through the docs and the many many arguments, but could not find a way to force relative imports for known local folders (or force absolute for those who like that better).
I guess it may not be safe to do that automatically, but if --known-local-folder are provided, I think it should be doable ?
Did I miss something in the docs ? I think it would be very useful as mixed relative/absolute imports are very ugly and refactoring unfriendly.
See below for some outputs and desired outputs.
Thanks for the great package !
File structure :
-- module
-- __init__.py
-- submodule.py
-- othersubmodule.py
Input :
from . import submodule
from module import othersubmodule
...
Output by default :
from module import othersubmodule
from . import submodule
...
Output with --known-local-folder module argument :
from . import submodule
from module import othersubmodule
...
Desired output with --known-local-folder module --force-relative (or something like this) argument :
from . import othersubmodule
from . import submodule
...
Desired output with --known-local-folder module --force-absolute (or something like this) argument :
from module import othersubmodule
from module import submodule
...
or force absolute for those who like that better
I had the same use-case and ended up creating absolufy-imports. @timothycrosley would this be a welcome addition to isort?
I'd be happy for isort to do this as well, or for it to link to the abs-imports project within the documentation
I work on a relatively large project made up of a number of "top-level modules", each of which is a separate library or service.
Our convention is to use relative imports within the same top-level module (for conciseness) and absolute imports outside of the top-level module (for explicitness). It works well with isort in the sense that once the separate sections for absolute and relative are enforced, it's easy to see which imports are local to the top-level module and which are not. It would be great if isort (or absolufy-imports) could enforce such a convention.
I realize that this might be too specific a use case, but just throwing the idea out there in case it strikes a chord with others.
Our convention is to use relative imports within the same top-level module (for conciseness) and absolute imports outside of the top-level module (for explicitness).
@brianmaissy if I've understood correctly, then the --keep-local-imports-relative flag in absolufy-imports should do that - does that work for you? If not, then perhaps share a little example of your directory structure and how the imports should look
I couldn't find documentation for --keep-local-imports-relative, but from what I was able to understand from the code and from trying it out, it means local to the same module. In my case, I want it to be local to the top-level module in the project, including submodules. For example:
/tmp/root$ tree
.
├── library1
│ ├── foo.py
│ └── subdirectory
│ ├── bar.py
│ └── baz.py
└── library2
└── qux.py
/tmp/root$ head library1/foo.py
from .subdirectory import bar
from root.library2 import qux
/tmp/root$ head library1/subdirectory/bar.py
from . import baz
from .. import foo
from root.library2 import qux
@brianmaissy what if we had
.
├── library1
│ ├── foo.py
│ ├── other_subdirectory
│ │ └── quox.py
│ └── subdirectory
│ ├── bar.py
│ └── baz.py
└── library2
└── qux.py
and library1/other_subdirectory/quox.py had
from library1.subdirectory import bar
?
Should it become from ..subdirectory import bar (i.e. local within library1) or from library1.subdirectory impor bar (i.e. absolute because quox.py is in other_subdirectory but bar.py is in subdirectory)?
If the former, then I think this would be require a flag like --submodules to which to pass library1:library2, and then maybe another flag --keep-local-within-submodules.
If the latter, then that requires yet another flag e.g. --keep-local-within-same-directory
The former. But I wouldn't want to need to specify submodules explicitly, I would want to be able to say "the whole first (or maybe Nth) level of the directory tree are submodules".
@brianmaissy OK, that makes sense - I think the default could be "the whole first level", and if anyone wants anything different they could pass the submodules (or the number of levels) via different flags
Are you sure about the
from .. import foo
import in library1/subdirectory/bar.py? Because the absolute version would be
from root import foo
, and as that's not an import from library1 then I think the rule of "relative within submodules" would mandate that it be from root import foo
I define all the submodules of library1 as being part of library1. My rule is "relative within top-level submodules" specifically, not within arbitrary submodules.
Sorry, yes, you're right, I confused myself, the absolute import there would have been from root.library1 import foo (perhaps if I'd bothered actually running absolufy-imports before commenting... :flushed: )
OK, @brianmaissy, as of v0.3.0, absolufy-imports has a --keep-submodules-relative flag - any issues / further requests please let me know at https://github.com/MarcoGorelli/absolufy-imports/issues
I'd be happy for isort to do this as well, or for it to link to the abs-imports project within the documentation
I'm about to start a new job and so don't have the resources to make sense of the isort source code and figure out how this would fit in - however, it's released under the permissive MIT license, so anyone is welcome to try to get it into isort (as Timothy said it'd be welcome)
thanks!
Just went looking for this option in isort, and found this issue! Would love for something like absolufy-imports to run from inside isort. Reason being end-user workflows, so that someone with an editor integration for isort will automatically get the right behavior if enabled. Similarly when using CI, it'd be one less check/pass, and it'd make it easier to help users fix it.
I'm not sure if anything has changed here in the last year, otherwise I'm curious whether a passable solution would be something like a plugin system (i.e. entrypoints-based discovery), and pre-/post-process hooks. This adds a layer of separation between isort and other tools, and reduces burden of implementation in isort itself. On the other hand, configuration becomes more complex and debuggability goes down.
@brianmaissy 's use case:
I would want to be able to say "the whole first (or maybe Nth) level of the directory tree are submodules".
was vaguely similar to mine, in a relative sense when --never is passed and I want to limit relatives to a depth of N. So I wrote this pull request for @MarcoGorelli 's absolufy-imports.
In case it helps anyone, we have since abandoned that convention in favor of the much simpler "all imports are absolute".
I found that once we were in the realm of automatically sorting/editing imports, it didn't matter much what the imports looked like anyway, since they were rarely human-read or -edited.
The grouping and conciseness we were trying to achieve were less valuable to us than having a simple and non-idiosyncratic convention which easily fit the default behavior of standard tooling.
Hey, I'm working in a fairly large Django codebase where we want to enforce absolute imports across apps and relative imports within apps. Rather than try to express that through the command line I'm thinking of making some kind of pre-processing hook where a plug-in can get a callback for each import and process it before any formatting or sorting. Does that sound like a viable strategy to solve this?