Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python settings for a project - circular imports / dependency injection

I've created a 'Django style' settings files for different environments. The settings files define some variables, and also serves as dependency injection for other modules.

So the structure is:

settings/
    ___init__.py
    base.py
    dev.py
    dev2.py
    prod.py

service/
    __init__.py
    service.py
    service_mock.py

And in settings/__init__.py I write:

settings_env = os.environ.get('PROJECT_SETTINGS', '')
if settings_env == 'prod':
   from .prod import *
elif settings_env == 'dev':
   from .dev import *

Each settings file define different some variables, and also import a class from service.py or service_mock.py, depends on the environment variable.
This works mostly fine.

Now, the problem is that the service.py cannot import the settings package, because the settings files import the service.py, so that will become a circular import.

As I see in Django it is solved by using import strings in the settings files, instead of actual imports. I don't really like the idea as I lose some of the IDE autocomplete features, also I'm not sure how to actually create the settings object that Django provides.

What are the solutions to this problem? Having a settings file that serves as a dependency injection container that imports modules, and get imported by the same modules? Preferably a simple solution.

like image 407
user3599803 Avatar asked Mar 05 '26 14:03

user3599803


1 Answers

A very common, and certainly easiest solution to circular imports, is to defer imports until you actually need them. For example, change

import settings
def func():
    settings.things

to

def func():
    import settings
    settings.things

If you absolutely must have module-global imports, there are various tricks you could use, such as

settings = None
def import_stuff():
    global settings
    import settings as s
    settings = s

or have a class

class Settings():
    mod = None        

    def __getattr__(self, attr):
        if self.mod is None:
            import settings
            self.mod = settings
        return getattr(self.mod, attr)

settings = Settings()

(or generalise for any module name)

like image 132
mdurant Avatar answered Mar 08 '26 02:03

mdurant