AttributeError вызван существующим атрибутом
Иногда я получаю следующие ошибки, когда мое приложение django пытается получить или сохранить что-то в кэше:
c = cache.get(pk)
File "/opt/python3/lib/python3.4/site-packages/django/core/cache/__init__.py", line 131, in __getattr__
return getattr(caches[DEFAULT_CACHE_ALIAS], name)
File "/opt/python3/lib/python3.4/site-packages/django/core/cache/__init__.py", line 113, in __getitem__
cache = _create_cache(alias)
File "/opt/python3/lib/python3.4/site-packages/django/core/cache/__init__.py", line 88, in _create_cache
return backend_cls(location, params)
File "/opt/python3/lib/python3.4/site-packages/django/core/cache/backends/memcached.py", line 185, in __init__
value_not_found_exception=pylibmc.NotFound)
AttributeError: 'module' object has no attribute 'NotFound'
но почему? у модуля есть атрибут, и большую часть времени он работает, нет файла с таким же именем, который мог бы это сломать, где искать причину этого?
>>> import pylibmc
>>> pylibmc.NotFound
<class '_pylibmc.NotFound'>
>>>
1 ответ
tl;dr: попробуйте импортировать pylibmc в месте запуска приложения, например uwsgi
или же manage.py
файл.
Я думаю, что это проблема многопоточности при импорте pylibmc
в ветке запроса внутри PyLibMCCache.__init__
в отличие от при запуске приложения. (IMO Django выполняет там импорт, потому что не все установки Django используют pylibmc
и, следовательно, они не должны навязывать это каждому приложению как зависимость)
Пока я недостаточно знаком с внутренностями того, как import
работает, я подозреваю, что происходит что-то вроде ниже
- поток № 1 пытается импортировать
pylibmc
- поток #1 помещает заполнитель в
sys.modules
заpylibmc
- поток № 2 пытается импортировать
pylibmc
->
AttributeError
Поднялся - поток #1 закончил обновление
sys.modules
и сейчасpylibmc.NotFound
доступен
В целом, Python препятствует загрузке модулей во время выполнения, а не к загрузке во время запуска.
акцент мой
Примечание. Для проектов, в которых время запуска является критическим, этот класс [
importlib.util.LazyLoader
] позволяет потенциально минимизировать стоимость загрузки модуля, если он никогда не используется. Для проектов, в которых время запуска не является обязательным, использование этого класса крайне нежелательно из-за того, что сообщения об ошибках, создаваемые во время загрузки, откладываются и, следовательно, происходят вне контекста.