1

I am working on a project in Flask and am having trouble with circular imports.

My app's structure looks like this:

.
├── api
│   ├── __init__.py
│   ├── schema.py
│   └── sender.py
├── app.py
├── config.py
├── README.md
├── run.sh
├── static
│   ├── css
│   │   ├── base.css
│   │   ├── index.css
│   │   └── model.css
│   └── index.html
├── templates
│   ├── base.html
│   ├── model.html
│   ├── schema_add.html
│   ├── schema.html
│   └── table.html
└── views
    ├── auth.py
    ├── error_handler.py
    ├── __init__.py
    ├── model.py
    ├── schema.py
    └── table.py

The source code is available on this

Here's a trace of error:
    Traceback (most recent call last):
  File "./../anton_temp/app.py", line 3, in <module>
    from views import *
  File 
"/home/shubham1172/Documents/Anton/anton_temp/views/__init__.py", line 
16, in <module>
    from .auth import *
  File "/home/shubham1172/Documents/Anton/anton_temp/views/auth.py", 
line 4, in <module>
    from app import setConnection, getConnection, closeConnection
  File "/home/shubham1172/Documents/Anton/anton_temp/app.py", line 4, 
in <module>
    from api import *
  File "/home/shubham1172/Documents/Anton/anton_temp/api/__init__.py", 
line 13, in <module>
    from .schema import *
  File "/home/shubham1172/Documents/Anton/anton_temp/api/schema.py", 
line 5, in <module>
    from app import getConnection
ImportError: cannot import name 'getConnection'

The problem is, I have to include my blueprints (views and api) in my app. The init file for these blueprints further include the py files which in turn has to include some functions from the app.

I read somewhere to include these functions in an external file, say extension.py and then call it from the blueprints, but my functions in app contains references to 'app' for its config object.

How do I fix this?

EDIT

As pointed out, I'll have to refactor my code. This shows an example of the same issue and the solution was given. However, the function in my extension would require a call to the app's config, i.e.

A.py

import B
from C import dependency

B.py

from C import dependency

C.py

def dependency():
    #Use A.config here <----------
    pass

Is there any way to solve this?

EDIT

I solved it by refactoring my code. I figured out that app.config can be exported to a different file with a simple function call.

C.py

obj = None
def setObj(object):
    obj = object

def dependency():
    #use obj now
    pass    

setObj(obj) can now be called from A.py!

Shubham Sharma
  • 714
  • 1
  • 8
  • 18

2 Answers2

3

App configuration import issues can be handled by flask's config dictionary.

In module C.py you can access application config by using flask's current_app object.

If A.py is your main app file:

# A.py
from flask import Flask
app = Flask(__name__
app.config['MY_CONFIG_KEY'] = 'something'

Then you can access app's configuration in C.py like this:

# C.py
from flask import current_app

def dependency():
    v = current_app.config['MY_CONFIG_KEY']

One important thing to note is that the flasky way of solving circular dependencies is by using Application Factories and Blueprints.

Clayton A. Alves
  • 388
  • 2
  • 10
1

One way to attempt to avoid this issue is to catch all the circular imports exceptions:

>>> try:
>>>    from mylib import myclass
>>> except:
>>>    pass
>>> try:
>>>     from mylib2 import myclass2
>>> except:
>>>     pass

Another solution, which I also don't like, is importing packges when you meed them. As a consequence, you should do the imports within the functions that require it:

>>> def func1():
>>>     from mylib import myclass
>>>     c = myclass()
>>>     ....
>>>
>>> def func2():
>>>     from mylib2 import myclass2
>>>     ...

Another possible sulution is to import all the blueprint's dependencies after you crete the app object. That means that you can safely import the object "app" because you delayed importing the dependencies.

In your main.py (or main file):

>>> from flask import Flask
>>> 
>>> app = Flask()
>>> 
>>> from . import views  # safe to import "app" from views.py now!
>>> 
>>> a = 3  # this generates circular dependency error from views.py. it hasn't been creted yet!!

In your views.py:

>>> from .main import app # safe to import "app". 
>>> 
>>> app.route("/")
>>> def hello_world():
>>>     ...

As you can appreciate, by the time that views.py calls main.py for the "app" object, it has already been created.

However, the best solution is to re-design your app. Put differently, if you ever find this issue in your app, it is a clear sign that you have not designed it propery and you need a different file system schema (or even a different architecture).

You can also refer to the factory design pattern available in the following link:

http://flask.pocoo.org/docs/0.12/patterns/appfactories/

Personally, I use factories and I haven't had any issues so far.