djangoapachemod-wsgipython-dataclassespython-3.10

Apache + mod_wsgi + Django + dataclasses stopped working after upgrade to python3.10


After upgrading to python3.10 code with dataclasses stopped working with mod_wsgi.

If a class is created with @dataclass decorator (with just a single attribute x: str) and then instantiated somewhere in apps.py at module level, an exception is thrown. The reason is missing generated __init__ method (I confirmed that everything works if I define __init__ manually). Unfortunately, in real project it's part of external library and dataclass is instantiated in imported module, so there's no way to define __init__ by hands. The code without changes (all packages have same versions too) works with python3.9.

To be clear, here's the code:

# models.py
from dataclasses import dataclass

@dataclass
class Test:
    foo: str

# apps.py
from django.apps import AppConfig
from .models import Test

Test('bar')  # Error is thrown here

class AppConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'app'

It seems like there's a bug somewhere. I also know that mod_wsgi only support python up to 3.8, according to data on pypi, but can't believe the answer is "Look for other wsgi solution or don't upgrade yet" (if so - tell me too, it might be true). You might notice that Django is of version 3.2 in requirements while 4.0 is available: I tried upgrading on this demo project, nothing has changed, plus django 3.2 claims to support python3.10 too (on the real project I can't upgrade due do dependencies using deprecated functions). So my questions are:

  1. Am I missing something that should be changed on upgrade to python 3.10 in this case?
  2. If it's some inner bug, where should I report it --- to python 3.10, to mod_wsgi or to Django?

The traceback is:

Traceback (most recent call last):
  File "/var/www/html/proof/proof/wsgi.py", line 16, in <module>
    application = get_wsgi_application()
  File "/usr/local/lib/python3.10/site-packages/django/core/wsgi.py", line 12, in get_wsgi_application
    django.setup(set_prefix=False)
  File "/usr/local/lib/python3.10/site-packages/django/__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/usr/local/lib/python3.10/site-packages/django/apps/registry.py", line 91, in populate
    app_config = AppConfig.create(entry)
  File "/usr/local/lib/python3.10/site-packages/django/apps/config.py", line 124, in create
    mod = import_module(mod_path)
  File "/usr/local/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/var/www/html/proof/app/apps.py", line 6, in <module>
    Test('bar')
TypeError: Test() takes no arguments

Here's repo with more details and full project layout, also Dockerfile present so you can reproduce easily.


Solution

  • In case anybody hits this somehow now, the bug was in python 3.10.1 core and was fixed in 3.10.2 and above.

    If such upgrade is not an option, it is necessary to update Apache configuration to avoid subinterpreter usage: set WSGIApplicationGroup %{GLOBAL} and ensure you're running in daemon mode.

    Here is the original issue with more details and the root bug in cpython here.