I have got a problem with circular imports in Python 3.9. I know there are numerous articles, questions and answers about this topic and I really have read a lot of them and about how to circumvent but I cannot figure out why it happens in my case. As far as I have understood one can circumvent circular imports by doing absolute imports:
This is my Stacktrace:
partially initialized module 'myproject.myapp.service.linepattern.corner' has no attribute 'Corner' (most likely due to a circular import)
File "/mnt/d/User/Programming/MyProject/myproject/myapp/service/linepattern/line.py", line 8, in Line
def __init__(self, cornerRight: cc.Corner, cornerLeft: cc.Corner) -> None:
File "/mnt/d/User/Programming/MyProject/myproject/myapp/service/linepattern/line.py", line 7, in <module>
class Line(ABC):
File "/mnt/d/User/Programming/MyProject/myproject/myapp/service/linepattern/corner.py", line 2, in <module>
import myproject.myapp.service.linepattern.line as cl
File "/mnt/d/User/Programming/MyProject/myproject/myapp/service/linepattern/cornermanager.py", line 2, in <module>
import myproject.myapp.service.linepattern.corner as cc
File "/mnt/d/User/Programming/MyProject/myproject/tests/myapp/test_cornermanager.py", line 3, in <module>
import myproject.myapp.service.linepattern.cornermanager as cm
and this is my file hierarchy
/mnt/d/User/Programming/MyProject
├── manage.py
├── myproject
│ ├── myapp
│ │ ├── __init__.py
│ │ ├── service
│ │ │ └── linepattern
│ │ │ ├── cornermanager.py
│ │ │ ├── corner.py
│ │ │ ├── line.py
│ │ │ ├── __init__.py
│ ├── settings.py
│ ├── tests
│ │ ├── myapp
│ │ │ ├── __init__.py
│ │ │ └── test_cornermanager.py
and finally my code
cornermanager.py
import myproject.myapp.service.linepattern.corner as cc
class CornerManager:
def __init__(self) -> None:
self.cornerList: List[cc.Corner]
for i in range(1, 2):
self.cornerList.append(cc.Corner(i, self))
for corner in self.cornerList:
corner.initializeLines()
def corner(self, cornerNr: int) -> cc.Corner:
return self.cornerList[cornerNr - 1]
corner.py
import myproject.myapp.service.linepattern.cornermanager as cm
import myproject.myapp.service.linepattern.line as cl
class Corner:
def __init__(self, cornerNumber: int, cornerManager: cm.CornerManager) -> None:
self.cornerNumber = cornerNumber
self.cornerManager = cornerManager
self.lineSet: Set[cl.Line]
def initializeLines(self) -> None:
#Add lines to the corner including its neighbor
if self.cornerNumber == 1:
self.lineSet.add(cl.Line(self, self.cornerManager.corner(2)))
elif self.cornerNumber == 2:
self.lineSet.add(cl.Line(self, self.cornerManager.corner(2)))
def addLine(self, line: cl.Line) -> None:
#Remove line from both corners
for corner in cl.cornerPair():
corner.lineSet.add(line)
def removeLine(self, line: cl.Line) -> None:
#Remove line from both corners
for corner in cl.cornerPair():
corner.lineSet.remove(line)
line.py
import myproject.myapp.service.linepattern.corner as cc
class Line:
def __init__(self, cornerRight: cc.Corner, cornerLeft: cc.Corner) -> None:
self.rightCorner = cornerRight
self.leftCorner = cornerLeft
def cornerPair(self) -> Tuple[cc.Corner, cc.Corner]:
#Return both corners
return (self.rightCorner, self.leftCorner)