I am learning python (fairly new) and decided to create a simple rock paper scissors bot as a mini side project to apply the lessons I've learned.
I have encountered an issue during my development:
Based on my understanding and some searches on stackoverflow (specifically this question), this happens due to the imports that I made. My files imports from one another. So when Python tries to load one while the other is unfinished, it crashes with "partially initialized module". I understood it's kind of a structural dependency problem. So my fix was to create another View that these two Views can call instead.
First, here's the logic flow that I did for my bot. I did this portion first, where the user chooses "How to Play", and another view if the user wants to continue or exit:

What I did was just loop the views. Here are my views. I'll add the relevant code only:
rps_views.py (the view the bot calls first)
import discord, datetime
from game.rock_paper_scissors.utils.game_logic import (
get_players_choice_value,
get_bots_choice_value,
rock_paper_scissors,
get_state_emoji
)
from game.rock_paper_scissors.views.rps_navigate import (
RpsNavigate
)
rps_nav_obj = RpsNavigate()
class RpsMainMenuView(discord.ui.View):
def __init__(self, *, timeout = 180):
super().__init__(timeout=timeout)
#How to play
@discord.ui.button(label="❔How to Play", style=discord.ButtonStyle.primary)
async def how_to_play(self, btn_interaction: discord.Interaction, button: discord.ui.Button):
await btn_interaction.response.send_message(
"Classic Rock, Paper, Scissors! Choose between the three, and we'll see if you win against me!",
view=rps_nav_obj.determine_view("HTP")
)
how_to_play_views.py
import discord
from game.rock_paper_scissors.views.rps_navigate import (
RpsNavigate
)
rps_nav_obj = RpsNavigate()
class HowToPlayView(discord.ui.View):
def __init__(self, *, timeout: float | None = 180):
super().__init__(timeout=timeout)
#Continue
@discord.ui.button(label="✅Continue", style=discord.ButtonStyle.success)
async def htp_continue(self, btn_interaction: discord.Interaction, button: discord.ui.Button):
await btn_interaction.response.send_message(
"Ready to Play Rock Paper Scissors?",
view=rps_nav_obj.determine_view("RPS")
)
#Exit
@discord.ui.button(label="🔚 Exit", style=discord.ButtonStyle.red)
async def htp_exit(self, btn_interaction:discord.Interaction, button: discord.ui.Button):
await btn_interaction.response.send_message(
"Goodbye!"
)
rps_navigate.py , I created this View so that the two can call this instead of being dependent on each other.
import discord
class RpsNavigate(discord.ui.View):
def __init__(self):
self = self
def determine_view(self, view_choice):
self.view_choice = view_choice
print(f"[RPS Navigate] Determining views to be passed: {self.view_choice}")
#initialize views
print("Initializing views...")
from game.rock_paper_scissors.views.rps_views import (
RpsMainMenuView,
RpsMovesView
)
from game.rock_paper_scissors.views.how_to_play_views import (
HowToPlayView
)
rps_main_menu_view = RpsMainMenuView()
how_to_play_view = HowToPlayView()
rps_moves_view = RpsMovesView()
if self.view_choice == "RPS":
return rps_main_menu_view
elif self.view_choice == "HTP":
return how_to_play_view
elif self.view_choice == "RMV":
return rps_moves_view
else:
return rps_main_menu_view
Since I mentioned earlier that rps_view.py is the first view my bot calls, I still encounter the circular issue import if I put the imports in the second line of rps_navigate.py. So I figured I'd add it inside the determine_view method. And my bot works perfectly. My question is, is there a better way to do this? Maybe it's because I'm also from Java, and I'm used that imports are at the top of the classes. Any advice and tips are appreciated!