[Django] #34807: importing `django.forms` causes circular import error

81 views
Skip to first unread message

Django

unread,
Aug 31, 2023, 12:40:34 PM8/31/23
to django-...@googlegroups.com
#34807: importing `django.forms` causes circular import error
-------------------------------------------+------------------------
Reporter: Collin Anderson | Owner: nobody
Type: Bug | Status: new
Component: Forms | Version: dev
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------------+------------------------
circular import between forms and models and choices.

before 500e01073adda32d514962 you could import forms just fine.

{{{
git checkout 500e01073adda32d514962^
python3 -c"from django.db import models" # works fine
python3 -c"from django import forms" # works fine
}}}


but since 500e01073adda32d514962 this hasn't worked due to circular
import.

{{{
git checkout 500e01073adda32d514962
python3 -c"from django.db import models" # works fine
python3 -c"from django import forms" # fails:

Traceback (most recent call last):
File "<string>", line 1, in <module>
File "django/forms/__init__.py", line 6, in <module>
from django.forms.boundfield import * # NOQA
File "django/forms/boundfield.py", line 5, in <module>
from django.forms.widgets import MultiWidget, Textarea, TextInput
File "django/forms/widgets.py", line 15, in <module>
from django.utils.choices import normalize_choices
File "django/utils/choices.py", line 3, in <module>
from django.db.models.enums import ChoicesMeta
File "django/db/models/__init__.py", line 3, in <module>
from django.db.models.aggregates import * # NOQA
File "django/db/models/aggregates.py", line 5, in <module>
from django.db.models.expressions import Case, Func, Star, Value, When
File "django/db/models/expressions.py", line 12, in <module>
from django.db.models import fields
File "django/db/models/fields/__init__.py", line 18, in <module>
from django.utils.choices import CallableChoiceIterator,
normalize_choices
ImportError: cannot import name 'CallableChoiceIterator' from partially
initialized module 'django.utils.choices' (most likely due to a circular
import) (django/utils/choices.py)

python3 -c"from django.utils import choices" # also fails:

Traceback (most recent call last):
File "<string>", line 1, in <module>
File "django/utils/choices.py", line 3, in <module>
from django.db.models.enums import ChoicesMeta
File "django/db/models/__init__.py", line 3, in <module>
from django.db.models.aggregates import * # NOQA
File "django/db/models/aggregates.py", line 5, in <module>
from django.db.models.expressions import Case, Func, Star, Value, When
File "django/db/models/expressions.py", line 12, in <module>
from django.db.models import fields
File "django/db/models/fields/__init__.py", line 10, in <module>
from django import forms
File "django/forms/__init__.py", line 6, in <module>
from django.forms.boundfield import * # NOQA
File "django/forms/boundfield.py", line 5, in <module>
from django.forms.widgets import MultiWidget, Textarea, TextInput
File "django/forms/widgets.py", line 15, in <module>
from django.utils.choices import normalize_choices
ImportError: cannot import name 'normalize_choices' from partially
initialized module 'django.utils.choices' (most likely due to a circular
import) (django/utils/choices.py)
}}}

Models has always had a dependency on Forms, but now Models depends on
Forms depends on `utils.choices` which depend on
`models.enums.ChoicesMeta`

Summary of the circle: `django.forms` -> 'forms.widgets` ->
`django.utils.choices` -> `django.db.models.enums` -> `models.__init__` ->
`models.aggregates` -> `models.fields` -> `django.forms`

If `django.db.models` is imported before forms then it happens to import
fine because the order of imports happens to lines up perfectally, but
it's very very fragile. `models.__init__` -> `models.aggregates` ->
`models.fields` -> `django.forms` -> 'forms.widgets` ->
`django.utils.choices` -> `django.db.models.enums`.

--
Ticket URL: <https://code.djangoproject.com/ticket/34807>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Aug 31, 2023, 3:40:04 PM8/31/23
to django-...@googlegroups.com
#34807: importing `django.forms` causes circular import error
---------------------------------+------------------------------------

Reporter: Collin Anderson | Owner: nobody
Type: Bug | Status: new
Component: Forms | Version: dev
Severity: Release blocker | Resolution:
Keywords: | Triage Stage: Accepted

Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
---------------------------------+------------------------------------
Changes (by Tim Graham):

* stage: Unreviewed => Accepted
* severity: Normal => Release blocker


Old description:

New description:

`models.aggregates` -> `models.fields` -> `django.forms`.

If `django.db.models` is imported before forms then it happens to import
fine because the order of imports happens to lines up perfectally, but
it's very very fragile. `models.__init__` -> `models.aggregates` ->
`models.fields` -> `django.forms` -> `forms.widgets` ->
`django.utils.choices` -> `django.db.models.enums`.

--

--
Ticket URL: <https://code.djangoproject.com/ticket/34807#comment:1>

Django

unread,
Aug 31, 2023, 3:48:41 PM8/31/23
to django-...@googlegroups.com
#34807: importing `django.forms` causes circular import error
-------------------------------------+-------------------------------------
Reporter: Collin Anderson | Owner: Natalia
| Bidart
Type: Bug | Status: assigned

Component: Forms | Version: dev
Severity: Release blocker | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* owner: nobody => Natalia Bidart
* status: new => assigned


--
Ticket URL: <https://code.djangoproject.com/ticket/34807#comment:2>

Django

unread,
Aug 31, 2023, 3:49:17 PM8/31/23
to django-...@googlegroups.com
#34807: importing `django.forms` causes circular import error
-------------------------------------+-------------------------------------
Reporter: Collin Anderson | Owner: Natalia
| Bidart
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Release blocker | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* cc: Nick Pope (added)


--
Ticket URL: <https://code.djangoproject.com/ticket/34807#comment:3>

Django

unread,
Aug 31, 2023, 4:01:25 PM8/31/23
to django-...@googlegroups.com
#34807: importing `django.forms` causes circular import error
-------------------------------------+-------------------------------------
Reporter: Collin Anderson | Owner: Natalia
| Bidart
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Release blocker | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Natalia Bidart):

One thing that I don't understand is that when using a Django shell, the
import works just fine. Tests also perform multiple `from django import
forms` and they do not fail. OTOH, I can reproduce with the provided:

{{{


python3 -c"from django import forms"
}}}

Does anyone understand why the difference? (my main question is why this
wasn't picked up by the tests or my IRL testing within a Django test
project)

--
Ticket URL: <https://code.djangoproject.com/ticket/34807#comment:4>

Django

unread,
Aug 31, 2023, 4:07:06 PM8/31/23
to django-...@googlegroups.com
#34807: importing `django.forms` causes circular import error
-------------------------------------+-------------------------------------
Reporter: Collin Anderson | Owner: Natalia
| Bidart
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Release blocker | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* has_patch: 0 => 1


Comment:

[https://github.com/django/django/pull/17215 PR]

--
Ticket URL: <https://code.djangoproject.com/ticket/34807#comment:5>

Django

unread,
Aug 31, 2023, 4:09:02 PM8/31/23
to django-...@googlegroups.com
#34807: importing `django.forms` causes circular import error
-------------------------------------+-------------------------------------
Reporter: Collin Anderson | Owner: Natalia
| Bidart
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Release blocker | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Collin Anderson):

`./manage.py shell` imports all models as part of `django.setup()`.

--
Ticket URL: <https://code.djangoproject.com/ticket/34807#comment:6>

Django

unread,
Aug 31, 2023, 9:23:01 PM8/31/23
to django-...@googlegroups.com
#34807: importing `django.forms` causes circular import error
-------------------------------------+-------------------------------------
Reporter: Collin Anderson | Owner: Natalia
| Bidart
Type: Bug | Status: assigned
Component: Forms | Version: dev
Severity: Release blocker | Resolution:
Keywords: | Triage Stage: Ready for
| checkin

Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* stage: Accepted => Ready for checkin


--
Ticket URL: <https://code.djangoproject.com/ticket/34807#comment:7>

Django

unread,
Aug 31, 2023, 11:44:48 PM8/31/23
to django-...@googlegroups.com
#34807: importing `django.forms` causes circular import error
-------------------------------------+-------------------------------------
Reporter: Collin Anderson | Owner: Natalia
| Bidart
Type: Bug | Status: closed
Component: Forms | Version: dev
Severity: Release blocker | Resolution: fixed

Keywords: | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by GitHub <noreply@…>):

* status: assigned => closed
* resolution: => fixed


Comment:

In [changeset:"9c6879284315d5119942355c340c3e48f6c65882" 9c68792]:
{{{
#!CommitTicketReference repository=""
revision="9c6879284315d5119942355c340c3e48f6c65882"
Fixed #34807 -- Avoided circular import between forms, models, and utils'
choices.

Thanks Collin Anderson for the report.

Regression in 500e01073adda32d5149624ee9a5cb7aa3d3583f.
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/34807#comment:8>

Reply all
Reply to author
Forward
0 new messages