Open stacey-gammon opened 2 years ago
Hi Stacey,
Great question.
The way I prevent circular dependencies is by writing layers contracts. But that involves making a decision about how the dependencies should flow, which is ultimately an act of design. If you've got a preexisting project you could try running my other tool Impulse on it to give you clues about where the dependencies are currently flowing.
I have been wondering about a more general contract that just detects circular dependencies, without any further input. But that would be based on my own definition of circular dependencies, which is essentially that, at each level of a package hierarchy, the descendants of each module don't have cycles. For example, if mypackage.foo.blue
imports mypackage.bar.green
, and mypackage.bar.yellow
imports mypackage.foo.orange
, I would view that as a circular dependency. But that's just my own opinion about how to structure Python projects.
Does that make sense?
For example, if mypackage.foo.blue imports mypackage.bar.green, and mypackage.bar.yellow imports mypackage.foo.orange, I would view that as a circular dependency. But that's just my own opinion about how to structure Python projects.
In my opinion ( :stuck_out_tongue: ) it's perfectly fine for software to have opinions. Developers won't be forced to use it, and it could inspire someone to create a different (potentially better) circular dependency detection algorithm.
Thinking a bit more about it, such a contract could look like this:
[importlinter:contract:my-contract]
name = My contract
type = modular
containers=
mypackage
This would check that there are no cycles between the child packages of mypackage
. For example, if mypackage.blue.one
imported mypackage.green.two
, and mypackage.green.three
imported mypackage.blue.four
, that would be illegal.
We could also add a 'depth' argument to get it to do the same deeper in the tree. For example if this had a depth of 2, it would check that there are no cycles between the children of each child. For example, within mypackage.blue
it wouldn't allow mypackage.blue.one.alpha
to import mypackage.blue.two.beta
if mypackage.blue.two.gamma
imports mypackage.blue.one.delta
.
What I like about this is that it requires less up-front design, people could just enable it on a new project and then the linter will automatically detect when the package stops being modular.
Hello! I noticed in your blog post about this package, you mention circular dependencies being a reason you created it. I couldn't find any examples of preventing circular dependencies using the built-in contracts, however.
Will I need to build my own, or do any of the three included contracts provide this functionality?