Трудность использования gettext_lazy в настройках Django 4 с django-stubs, приводящая к циклу импорта и ошибке вывода типа mypy
Проблема:
Ошибка mypy:
Возникшая ошибка mypy выглядит следующим образом:
error:Import cycle from Django settings module prevents type inference for 'LANGUAGES' [misc]
Я столкнулся с проблемой использования gettext_lazy
в настройках Django 4 при использовании django-stubs. Недавнее обновление Django 4 принесло изменения в типизацию gettext_lazy
, где старый тип возврата был str
, а новый тип _StrPromise
. Проблема возникает из-за того, что _StrPromise
определен в файле "django-stubs/utils/functional.pyi", а внутри этого файла также есть импорт django.db.model
, который импортирует настройки. Это создает круговой импорт.
текущая версия модуля:
типирование mypy = "1.7" django-stubs = "4.2.7"
Зависимости Django Django = "4.2.10"
Ищу совета по более чистому и устойчивому решению проблемы циклического импорта и ошибки mypy в контексте настроек Django 4 с обновленной типизацией gettext_lazy
. Любые соображения или альтернативные подходы будут высоко оценены.
Возможные решения:
Было рассмотрено несколько решений, но ни одно из них не является идеальным:
Отключение Mypy-типирования для настроек: Отключение типизации Mypy для настроек - это обходной путь, но он может свести на нет преимущества статической типизации.
Удалите
gettext_lazy
из настроек: Другой вариант - удалитьgettext_lazy
из настроек. Однако это противоречит текущим рекомендациям в документации Django 4 и 5, которые по-прежнему выступают за использованиеgettext_lazy
в настройкахLANGUAGE
.
Разбираться с круговым импортом и проблемами типизации Mypy в настройках Django, особенно с обновленной типизацией gettext_lazy в Django 4, действительно может быть непросто. Давайте рассмотрим несколько альтернативных подходов к решению этой проблемы:
Отдельный модуль конфигурации: Вместо того чтобы определять настройки непосредственно в файле settings.py, создайте отдельный модуль, скажем settings_config.py, в котором вы будете определять свои настройки. Этот модуль не должен импортировать ничего из Django models или других модулей, что может привести к циклическому импорту. Затем, в вашем settings.py, импортируйте настройки из settings_config.py. Такое разделение поможет разорвать круговую зависимость.
Пример:
# settings_config.py
from django.utils.translation import gettext_lazy as _
LANGUAGE_CODE = 'en-us'
LANGUAGES = [
('en', _('English')),
('fr', _('French')),
]
# settings.py
from .settings_config import *
Используйте строки вместо констант: Вместо прямых ссылок на gettext_lazy в настройках используйте строки, которые будут разрешены во время выполнения. Хотя это и не дает преимуществ статической типизации, но позволяет избежать циклического импорта. Пример:
# settings.py
LANGUAGES = [
('en', 'English'),
('fr', 'French'),
]
Пользовательская заглушка типизации: Вы можете создать пользовательскую заглушку для gettext_lazy или _StrPromise, если существующие заглушки вызывают циклический импорт. Этот подход предполагает определение необходимых типов в отдельном файле без импорта проблемных модулей. Пример:
# custom_gettext_lazy.pyi
from typing import Any
class _StrPromise:
def __init__(self, message: str):
pass
def __str__(self) -> str:
...
def gettext_lazy(message: str) -> _StrPromise: ...
Тогда вы сможете использовать gettext_lazy в настройках с правильным набором текста, и, надеюсь, это позволит избежать проблемы кругового импорта.
Эти подходы должны помочь смягчить проблему циклического импорта и при этом позволить вам использовать gettext_lazy в настройках Django. Выберите тот, который лучше всего соответствует структуре вашего проекта и вашим предпочтениям.
Также только что столкнулся с этим. Это объясняется в django-stubs readme.
Если вы столкнулись с этой ошибкой в своем коде, вы можете либо привести Promise к str (что приведет к оценке перевода), либо использовать типы StrPromise или StrOrPromise из django-stubs-ext в подсказках типов.
Чтобы получить подсказки типов, необходимо установить расширение stubs:
pip install django-stubs-ext
Затем вы можете импортировать подсказки:
from django_stubs_ext import StrPromise
my_lazy_str: StrPromise = gettext_lazy("foobar")