Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New contract type: modular #133

Open
stacey-gammon opened this issue Sep 14, 2022 · 3 comments · May be fixed by #221
Open

New contract type: modular #133

stacey-gammon opened this issue Sep 14, 2022 · 3 comments · May be fixed by #221

Comments

@stacey-gammon
Copy link

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?

@seddonym
Copy link
Owner

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?

@mwgamble
Copy link
Contributor

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 ( 😛 ) 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.

@seddonym seddonym changed the title Built-in contract to prevent circular imports New contract type: modular Feb 3, 2023
@seddonym
Copy link
Owner

seddonym commented Feb 3, 2023

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.

@sbrugman sbrugman linked a pull request Feb 23, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants