Aaron Aaron - 8 days ago 9
Python Question

Temporarily modifying `builtins` while splitting a django settings file

I'm starting a django 1.10 project and would like to split the settings file. I was unsatisfied with any existing solutions.


  1. I do not want to be able to override
    string
    /
    boolean
    /
    dict
    settings from one file in another. Each
    string
    /
    boolean
    /
    dict
    setting should be set in only one place. This makes it easy to keep track of where things are defined.

  2. I do not want to have to manually extend
    tuple
    /
    list
    settings, e.g.
    INSTALLED_APPS += (test_app)
    . This seems to be messy and requires me to keep track of whether a list or tuple was used in the other file.

  3. I do not want to have to
    import os
    and define
    BASE_DIR
    in multiple files. DRY.



My solution, having looked at many others, is to replace
settings.py
with a directory containing
local_settings.py
,
common_settings.py
and
__init__.py
.

In
__init__.py
, I
import os
and calculate
BASE_DIR
. I then

import builtins
builtins.BASE_DIR = BASE_DIR
builtins.os = os

from .common_settings import *
from . import local_settings

# At this point both modules have run and we no longer need to be messing
# with the builtins namespace.
del builtins.BASE_DIR
del builtins.os
del builtins


I then loop over
dir(local_settings)
and mess with
globals()
to achieve the first two requirements (I can post the whole thing if requested but I'm interested in my use of
builtins
).

Is this use of
builtins
too evil? What can break it. Obviously if either identifier clashs with an attribute of a later version of
builtins
, then this code would break python. If a function that uses either of these identifiers ended up in one of the settings files and it was later called, then that would break.

I don't see either of those happening though. Is there a potential problem that I'm not seeing?

Answer

The main problem with modifying builtins in this way is that it adds non-local reasoning to your code for no good reason. The behavior of the common/local settings modules now implicitly depends on what happens in the module that imports them. That's bad.

Essentially, you need to get rid of your requirement #3.

  • Importing os in each module isn't "repeating yourself" because each module imports os into its own namespace. That's just how Python works.

  • You're right to want to only define BASE_DIR once, but the correct way to do this is to define the variable in one module (say basedir.py) and then explicitly import that variable (from basedir import BASE_DIR) into each module that uses it.

Comments