tags:

views:

129

answers:

4

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.
A: 

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.

msw
Thanks msw. But I don't want to create my own settings, I want to change the django's core behavior around settings, by, e.g., storing some settings in the database and making them editable through the admin. I added some of this rationale to the question to clarify.
zlovelady
And I do want to sub-class and override, but I want to do it in a file that lives outside of the original package directory structure
zlovelady
A: 

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.

Vinay Sajip
django-values is nice. I've used it before. But I'm really not looking for a solution specific to django settings. I just included that as an example. I'm hoping to get some insight on if this is possible through pythons import.
zlovelady
+1  A: 
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.

freegnu
Awesome. Didn't know it was that easy. Thanks.
zlovelady
That example using django.conf.settings is an inappropriate example as django uses getters and setters to automatically override the global settings with the local ones. The django docs specifically say not to do what I did in that example because the local settings automatically override the global settings. Watch out for side effects if you use the above method for that specific settings object in django.
freegnu
+1  A: 

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

Thomas Leonard
With this approach, could I override a module within a package? I want `import foo.bar.somemodule` to load my version of somemodule.
zlovelady
It seems to work. I've updated the answer...
Thomas Leonard