Create rule S7197 (#4739)
This commit is contained in:
parent
b6559d9c46
commit
51dd4ca773
@ -1,25 +1,2 @@
|
||||
{
|
||||
"title": "Circular file imports should be resolved",
|
||||
"type": "CODE_SMELL",
|
||||
"status": "ready",
|
||||
"remediation": {
|
||||
"func": "Constant\/Issue",
|
||||
"constantCost": "0min"
|
||||
},
|
||||
"tags": [
|
||||
"architecture",
|
||||
"design"
|
||||
],
|
||||
"defaultSeverity": "Critical",
|
||||
"ruleSpecification": "RSPEC-7197",
|
||||
"sqKey": "S7197",
|
||||
"scope": "All",
|
||||
"defaultQualityProfiles": ["Sonar way"],
|
||||
"quickfix": "infeasible",
|
||||
"code": {
|
||||
"impacts": {
|
||||
"MAINTAINABILITY": "HIGH"
|
||||
},
|
||||
"attribute": "MODULAR"
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,25 @@
|
||||
{
|
||||
"title": "Circular file imports should be resolved",
|
||||
"type": "CODE_SMELL",
|
||||
"status": "ready",
|
||||
"remediation": {
|
||||
"func": "Constant\/Issue",
|
||||
"constantCost": "0min"
|
||||
},
|
||||
"tags": [
|
||||
"architecture",
|
||||
"design"
|
||||
],
|
||||
"defaultSeverity": "Critical",
|
||||
"ruleSpecification": "RSPEC-7197",
|
||||
"sqKey": "S7197",
|
||||
"scope": "All",
|
||||
"defaultQualityProfiles": ["Sonar way"],
|
||||
"quickfix": "infeasible",
|
||||
"code": {
|
||||
"impacts": {
|
||||
"MAINTAINABILITY": "HIGH"
|
||||
},
|
||||
"attribute": "MODULAR"
|
||||
}
|
||||
}
|
||||
|
2
rules/S7197/python/metadata.json
Normal file
2
rules/S7197/python/metadata.json
Normal file
@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
74
rules/S7197/python/rule.adoc
Normal file
74
rules/S7197/python/rule.adoc
Normal file
@ -0,0 +1,74 @@
|
||||
This rule reports circular dependencies between source modules, including indirect cycles spanning multiple modules caused by circular imports.
|
||||
|
||||
== Why is this an issue?
|
||||
|
||||
Circular dependencies occur when two or more modules import each other, either directly or indirectly.
|
||||
This creates a dependency structure that lacks a clear hierarchy, making the codebase harder to understand and maintain.
|
||||
Additionally, the order in which circular imports are resolved is not guaranteed, which can lead to unpredictable behavior and runtime errors.
|
||||
|
||||
=== What is the potential impact?
|
||||
|
||||
Circular dependencies increase the complexity of the code architecture, reducing readability, extensibility, and maintainability.
|
||||
As the project grows, these dependencies can spread, further complicating the architecture and increasing technical debt.
|
||||
Over time, resolving these dependencies becomes increasingly difficult.
|
||||
|
||||
== How to fix it
|
||||
|
||||
1. **Refactor shared functionality**: If multiple modules share similar functionality, consider moving that functionality to a separate module that both can import. This allows each module to depend on the shared module rather than on each other.
|
||||
|
||||
2. **Use dependency inversion:** Instead of directly importing modules that create circular dependencies, use dependency inversion by passing necessary functions or objects as parameters. This breaks the circular reference and makes the code more modular and testable.
|
||||
|
||||
3. **Split responsibilities**: Evaluate whether each file is handling too many responsibilities. If so, break them down into smaller, more focused modules. This reduces circular dependencies and ensures that your code is easier to manage and extend.
|
||||
|
||||
=== Code examples
|
||||
|
||||
==== Noncompliant code example
|
||||
|
||||
This example shows a circular dependency between modules `order.py` and `customer.py`:
|
||||
|
||||
[source,python,diff-id=1,diff-type=noncompliant]
|
||||
----
|
||||
# order.py
|
||||
from customer import Customer # Noncompliant
|
||||
class Order:
|
||||
def __init__(self, customer: Customer):
|
||||
self.customer = customer
|
||||
|
||||
# customer.py
|
||||
from order import Order # Noncompliant
|
||||
class Customer:
|
||||
def __init__(self, order: Order):
|
||||
self.order = order
|
||||
----
|
||||
|
||||
==== Compliant solution
|
||||
|
||||
The issue can be resolved by refactoring the code structure.
|
||||
Instead of `Customer` and `Order` referencing each other, customers by order and orders by customer can be aggregated using an addition class `CustomerOrder`.
|
||||
|
||||
[source,python,diff-id=1,diff-type=compliant]
|
||||
----
|
||||
# order.py
|
||||
class Order:
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
# customer.py
|
||||
class Customer:
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
# customer_order.py
|
||||
from customer import Customer
|
||||
from order import Order
|
||||
class CustomerOrder:
|
||||
def __init__(self, customer: Customer, order: Order):
|
||||
self.customer = customer
|
||||
self.order = order
|
||||
----
|
||||
|
||||
== Resources
|
||||
|
||||
- Wikipedia - https://en.wikipedia.org/wiki/Acyclic_dependencies_principle[Acyclic dependencies principle]
|
||||
- STAN - https://stan4j.com/advanced/adp/[Acyclic dependencies principle]
|
||||
- RSPEC - https://sonarsource.github.io/rspec/#/rspec/S7091/java[S7091: Circular dependencies between classes across package boundaries should be resolved]
|
Loading…
x
Reference in New Issue
Block a user