zlovelady
zlovelady

Reputation: 4097

Python: How can I override one module in a package with a modified version that lives outside the package?

I would like to update one module in a python package with my own version of the module, with the following conditions:

Here's an example of what I'd like to do using specifics from django, because that's where this problem has arisen for me:

Say this is my project structure

django/
  ... the original, unadulterated django package ...
local_django/
  conf/
    settings.py
myproject/
  __init__.py
  myapp/
    myfile.py

And then in myfile.py

# These imports should fetch modules from the original django package
from django import models
from django.core.urlresolvers import reverse

# I would like this following import statement to grab a custom version of settings 
# that I define in local_django/conf/settings.py 
from django.conf import settings

def foo():
  return settings.some_setting

Can I do some magic with the __import__ statement in myproject/__init__.py to accomplish this? Is there a more "pythonic" way to achieve this?

Update - Why do I want to do this

Here's the scenario where I think this makes sense.

Upvotes: 20

Views: 18394

Answers (5)

alpheus
alpheus

Reputation: 317

I was introduced to this about two or three days ago, but one technique of doing this very thing is to use a Python Virtual Environment. This allows you to designate the version of Python you are using, as well as the versions of the modules you want to install, and to be able to swap between different versions and projects relatively easy; it also allows you to install a Python system where you might not otherwise have the permissions needed to install something.

You can learn more about virtualenv from virtualenv info.

There's also a "virtualenvwrapper" package, that provides tools to more easily swap between environments.

Admittedly, I don't have much experience with this, and I found this question in an attempt to understand how to override a Python Standard Library with a custom-built version--so it's not perfect. But I am nonetheless impressed with its ability to specify what you install, down to the version of an individual module!

Upvotes: 0

freegnu
freegnu

Reputation: 813

import local_django.conf
import django.conf
django.conf.settings = local_django.conf.settings

Modules are singletons. Modules are only initialized/loaded once. You need to do this before importing the modules that use django.conf.settings for them to pick up the change.

Read this link for more info to see if there is a more standard approach with django as the docs specifically recommend against doing it the way I show above for the settings object. http://docs.djangoproject.com/en/dev/topics/settings/ It should work fine for other objects and modules.

Upvotes: 1

Thomas Leonard
Thomas Leonard

Reputation: 7196

Just set the entry in sys.modules before anything else imports it:

import sys
import myreplacement
sys.modules["original"] = myreplacement

Then, when someone does "import original", they'll get your version instead.

If you want to replace a submodule, you can do it like this:

import sys
import thepackage
sys.modules["thepackage"].submodule = myreplacement
sys.modules["thepackage.submodule"] = myreplacement

Then "from thepackage import submodule" or "import thepackage.submodule" will give "myreplacement".

Upvotes: 21

Vinay Sajip
Vinay Sajip

Reputation: 99355

Perhaps you can make use of the django-values project, which provides a dbsettings app, which ...

... allows placeholders for settings to be defined in Python, while their values are set by staff using an editor while the server is up and running. Many value types are available, and they each map to a native Python type, so model methods and other Python code can access them as standard class attributes.

Much effort has also been made to reduce the overhead of this feature, so that the database is only queried once during each server restart, and only updated when the values themselves are updated.

NOTE: This is not intended as a replacement for settings.py. This is designed for values that are expected to change based on the needs of the site or its users, so that such changes don't require so much as a restart. settings.py is still the place to go for settings that will only vary by project.

One way of looking at it is that settings.py is for those things that are required from a technical perspective (database connections, installed applications, avaialble middleware, etc), while dbsettings is best for those things that are required due to organizational policy (quotas, minimum requirements, etc). That way, programmers maintain the technical requirements, while administrators maintain organizational policy.

The project is by Marty Alchin (who wrote the Pro Django book) and does not require any changes to Django code - it's a standard Django application.

Upvotes: 1

msw
msw

Reputation: 43487

In this particular case, 'twould be best to follow the prescribed mechanism for creating your own settings.

In the general case, mucking with imports is guaranteed to confuse your reader (who may be you). The pythonic way to alter class behavior is to sub-class and override.

Upvotes: 0

Related Questions