Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: How to refactor circular imports

I've got a thing that you can do engine.setState(<state class>) and it will instantiate the class type you give it and start running on the new state.

In SelectFileState there is a button to go to NewFileState, and on NewFileState, there is a button to go back to SelectFileState.

Now, at the beginning of SelectFileState, I'm importing NewFileState (So I can later in the class do engine.setState(NewFileState). At the beginning of NewFileState, I'm also importing SelectFileState (So I can later go back to SelectFileState).

However, this creates a circular import, as described in some other posts. Some say that circular imports are indicators bad design, and should be refactored..

I know that I can just fix this problem by importing SelectFileState right before I need to use it, but I'd rather do things the right way and refactor it.

Now I'm wondering though.. How would you refactor that out?

Thanks.

Edit: Pydsigner suggests that I merge the two files into one, as they are both very related to each other. However, I cannot put EVERY state that has a circular dependency into one file, so there's got to be a better method for that. Any ideas?

2Edit: I'm circumventing this problem for now by not using the from x import y syntax, and instead just doing import x. This is not a preferable solution, and I'd like to know the "Pythonic" way to fix this kind of thing. Just merging files together can't be the fix forever.

The code:

SelectFileState

from states.state import State
from states.newfilestate import NewFileState

from elements.poster import Poster
from elements.label import Label
from elements.button import Button
from elements.trifader import TriFader

import glob
import os

class SelectFileState(State):
    def __init__(self, engine):
        super().__init__(engine)

    def create(self):
        self.engine.createElement((0, 0), Poster(self.engine.getImage('gui_loadsave')), 1)
        self.engine.createElement((168, 30), Label("Load a game", 40), 2)
        self.engine.createElement((400, 470), Button("New save", code=self.engine.createElement, args=((0, 0), TriFader(NewFileState, False), -240)), 3)

        ycounter = 150

        globs = glob.glob("save\\*.mcw")
        for file in globs:
            self.engine.createElement((200, ycounter), Button(os.path.basename(file)[:-4]), 2)
            ycounter += 50

NewFileState

from states.state import State
from states.selectfilestate import SelectFileState

from elements.poster import Poster
from elements.label import Label
from elements.button import Button
from elements.inputbox import InputBox
from elements.trifader import TriFader


class NewFileState(State):
    def __init__(self, engine):
        super().__init__(engine)

    def create(self):
        self.engine.createElement((0, 0), Poster(self.engine.getImage('gui_loadsave')), 1)
        self.engine.createElement((135, 30), Label("Make a new save", 40), 2)

        self.lvlname = self.engine.createElement((180, 212), InputBox(length=25, text="World name"), 2)
        self.engine.createElement((200, 240), Button(text="Ok", code=self.createSave, args=()), 2)

    def createSave(self):
        open("save\\" + self.lvlname.getText() + ".mcw", 'w')
        self.engine.createElement((0, 0), TriFader(SelectFileState), -240)
like image 860
Name McChange Avatar asked Nov 13 '12 02:11

Name McChange


People also ask

How do I get rid of cyclic dependency in Python?

The easiest way to fix this is to move the path import to the end of the node module. Save this answer.

How do you import circular?

In simplest terms, a circular import occurs when module A tries to import and use an object from module B, while module B tries to import and use an object from module A. We'll run the code from run.py, which just imports a.py. As you can see, we get an exception as soon as b.py tries to import a.py.


2 Answers

Without seeing code, what would make the most sense is to merge the two files. If they are that closely intertwined, you could probably put them together without anything really oddly out of place.

like image 183
pydsigner Avatar answered Sep 30 '22 15:09

pydsigner


In Python imports don't have to appear at the beginning of module. In fact they can appear in functions, so in NewFileState.py you could move the import of SelectFileState into NewFileState.create and you could make a similar change to SelectFileState.py

like image 24
Josh Heitzman Avatar answered Sep 30 '22 16:09

Josh Heitzman