Should I be strict about circular dependencies between sub-packages?
In perfect world I would treat every sub-package in my codebase the same way I treat modules. This is similar to a situation described in this question for Java.
Following this example I have parsing package with logic for parsing some text and storage package for DB-connection or data structures. For whatever reason parsing needs to import Record interface from storage and storage needs to use a Parser from parsing. This creates a circular dependency that would work fine, as long as we split code into separate files in each sub-package.
My rule of thumb would be to avoid circular dependencies at all cost. You can almost always reorganize the code to avoid such an issue. I was just wondering how strict should we be about it? Do we have linters/static analyzers that could detect those issues? Is it worth it?
As a bonus topic and motivation for discussion I'll mention just one more issue, which is reimporting stuff in __init__.py . For libraries, we are used to have a nice external modules structure even though code may be super-chaotic internally. We sometimes do it for our sub-packages to make refactoring easier in the future. For example our parser could be directly importable with something like:
from parsing.query_parser import QueryParser
To make it a bit cleaner for the rest of the codebase we may insert this line into parsing/__init__.py and then just access our parser as parsing.QueryParser. I personally like this approach, but unfortunately it causes circular import error when we use it in the setting described above.
Posted Jan 4, 2023, 6:21 PM UTC