I have the following structure for a package:
/prog
-- /ui
---- /menus
------ __init__.py
------ main_menu.py
------ file_menu.py
-- __init__.py
__init__.py
prog.py
These are my import/classes statements:
prog.py
:
from prog.ui.menus import MainMenu
/prog/ui/menus/__init__.py
:
from prog.ui.menus.file_menu import FileMenu
from prog.ui.menus.main_menu import MainMenu
main_menu.py
:
import tkinter as tk
from prog.ui.menus import FileMenu
class MainMenu(tk.Menu):
def __init__(self, master: tk.Tk, **kwargs):
super().__init__(master, **kwargs)
self.add_cascade(label='File', menu=FileMenu(self, tearoff=False))
[...]
file_menu.py
:
import tkinter as tk
from prog.ui.menus import MainMenu
class FileMenu(tk.Menu):
def __init__(self, master: MainMenu, **kwargs):
super().__init__(master, **kwargs)
self.add_command(label='Settings')
[...]
This will lead to a circular import problem in the sequence:
prog.py
-> __init__.py
-> main_menu.py
-> file_menu.py
-> main_menu.py
-> [...]
From several searches it was suggested to update the imports to such:
file_menu.py
import tkinter as tk
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from prog.ui.menus import MainMenu
class FileMenu(tk.Menu):
def __init__(self, master: 'MainMenu', **kwargs):
super().__init__(master, **kwargs)
self.add_command(label='Settings')
[...]
I've read the TYPE_CHECKING docs and the mypy docs on the usage, but I do not follow how using this conditional resolves the cycle. Yes, at runtime it works because it evaluates to False
so that is an "operational resolution", but how does it not reappear during type checking:
The TYPE_CHECKING constant defined by the typing module is False at runtime but True while type checking.
I don't know a great deal about mypy, thus I fail to see how once the conditional evaluates to True
that the issue will not reappear. What occurs differently between "runtime" and "type checking"? Does the process of "type checking" mean code is not executed?
Notes:
This is not a circular import dependency problem so dependency injection isn't needed
This is strictly a cycle induced by type hinting for static analysis
I am aware of the following import options (which work just fine):
Replace
from [...] import [...]
withimport [...]
Conduct imports in
MainMenu.__init__
and leavefile_menu.py
alone