Python: как я могу переопределить один модуль в пакете с измененной версией, которая живет за пределами пакета?

Я хотел бы обновить один модуль в пакете python с моей собственной версией модуля со следующими условиями:

  • Я хочу, чтобы мой обновленный модуль находился вне исходного пакета (либо потому, что у меня нет доступа к источнику пакета, либо потому, что я хочу сохранить свои локальные изменения в отдельном репо и т.д.).
  • Я хочу, чтобы инструкции import, относящиеся к исходному пакету/модулю, разрешали мой локальный модуль

Вот пример того, что я хотел бы сделать, используя специфику из django, потому что там, где эта проблема возникла для меня:

Скажите, что это моя структура проекта

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

И затем в файле 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

Можно ли сделать некоторую магию с помощью инструкции __import__ в myproject/__init__.py, чтобы выполнить это? Есть ли более "питонический" способ достичь этого?

Обновление - для чего я хочу это сделать

Вот сценарий, в котором я думаю, что это имеет смысл.

  • Я запускаю сайт, основанный на django, на сервере с предустановленным django глобально. В этом случае я не могу изменить фактический источник django.
  • В моем проекте django используются сторонние приложения для повторного использования, и я не хочу изменять imports во всех этих приложениях для импорта, например mycustomsettings. Я хочу, чтобы приложения, многократно используемые в блаженном порядке, не знали, что я изменил реализацию django.conf.settings.

Ответ 1

Просто установите запись в sys.modules прежде, чем что-либо еще импортирует ее:

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

Затем, когда кто-то "импортирует оригинал", они вместо этого получат вашу версию.

Если вы хотите заменить подмодуль, вы можете сделать это следующим образом:

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

Затем "из подмодуля импорта пакета" или "import thepackage.submodule" предоставит "myreplacement".

Ответ 2

Возможно, вы можете использовать проект django-values, который предоставляет приложение dbsettings, которое...

... позволяет заполнителям для настроек быть определенные в Python, тогда как их значения устанавливаются персоналом, используя редактор, в то время как сервер запущен и работает. Многие доступны типы значений, и они каждая карта относится к родному типу Python, поэтому методы модели и другие коды Python могут получить к ним доступ в качестве стандартного класса атрибуты.

Были также предприняты значительные усилия для уменьшить накладные расходы этой функции, так что база данных запрашивается только один раз во время перезапуска сервера и только обновляется, когда значения сами обновляются.

ПРИМЕЧАНИЕ. Это не предназначено для замена для settings.py. Это предназначенные для ожидаемых значений для изменения в зависимости от потребностей сайта или его пользователей, чтобы такие изменения не требуют перезапуск. settings.py по-прежнему место для установки параметров, которые будут зависит только от проекта.

Один из способов взглянуть на это - это то, что settings.py - это те вещи, которые требуются от технической перспективы (соединения с базой данных, установленные приложения, avaialble промежуточное ПО и т.д.), тогда как dbsettings лучше всего для тех вещей, которые требуется из-за организационной политики (квоты, минимальные требования и т.д.). Таким образом, программисты поддерживают технических требований, в то время как администраторы поддерживают организационные политика.

Проект Марти Алчин (который написал книгу Pro Django) и не требует никаких изменений в коде Django - это стандартное приложение Django.

Ответ 3

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

Модули являются одиночными. Модули только инициализируются/загружаются один раз. Вам нужно сделать это, прежде чем импортировать модули, которые используют django.conf.settings, чтобы они могли получить изменения.

Прочтите эту ссылку для получения дополнительной информации, чтобы узнать, существует ли более стандартный подход с django, поскольку документы рекомендуют не делать это так, как показано выше для объекта настроек. http://docs.djangoproject.com/en/dev/topics/settings/ Он должен отлично работать для других объектов и модулей.

Ответ 4

В данном конкретном случае лучше всего следовать предписанному механизму для создания собственных настроек.

В общем случае, гарантируя, что уловка с импортом путает вашего читателя (кто может быть вами). Питонический способ изменить поведение класса - это подкласс и переопределение.

Ответ 5

Я познакомился с этим примерно два или три дня назад, но одна из тех самых процедур, которые вы делаете, - это использовать виртуальную среду Python. Это позволяет вам обозначить версию используемого вами Python, а также версии модулей, которые вы хотите установить, и иметь возможность обмениваться между различными версиями и проектами относительно легко; он также позволяет вам установить систему Python, где у вас могут быть другие разрешения, необходимые для установки чего-либо.

Вы можете узнать больше о virtualenv из virtualenv info.

Также есть пакет "virtualenvwrapper", который предоставляет инструменты для более легкой замены между средами.

По общему признанию, у меня нет большого опыта в этом, и я нашел этот вопрос, пытаясь понять, как переопределить стандартную библиотеку Python с пользовательской версией, поэтому он не идеален. Но я тем не менее впечатлен своей способностью указывать, что вы устанавливаете, вплоть до версии отдельного модуля!