Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

django

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

  • I want my updated module to live outside of the original package (either because I don't have access to the package source, or because I want to keep my local modifications in a separate repo, etc).
  • I want import statements that refer to original package/module to resolve to my local module

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.

  • I am launching a django powered website on a server that has django pre-installed globally. I can't modify the actual django source, in this case.
  • My django project uses 3rd party reusable apps, and I don't want to change imports in all of those apps to import, for instance mycustomsettings. I want to leave the reusable apps blissfully ignorant that I have changed the implementation of django.conf.settings.
like image 568
zlovelady Avatar asked May 22 '10 05:05

zlovelady


3 Answers

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".

like image 165
Thomas Leonard Avatar answered Nov 15 '22 23:11

Thomas Leonard


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.

like image 44
Vinay Sajip Avatar answered Nov 15 '22 22:11

Vinay Sajip


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.

like image 25
freegnu Avatar answered Nov 15 '22 21:11

freegnu