Трудность использования 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. Любые соображения или альтернативные подходы будут высоко оценены.

Возможные решения:

Было рассмотрено несколько решений, но ни одно из них не является идеальным:

  1. Отключение Mypy-типирования для настроек: Отключение типизации Mypy для настроек - это обходной путь, но он может свести на нет преимущества статической типизации.

  2. Удалите 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")
Вернуться на верх