#27890: runtests.py cleanup exception on Python 3.6
---------------------------------------------+------------------------
               Reporter:  Vytis Banaitis     |          Owner:  nobody
                   Type:  Bug                |         Status:  new
              Component:  Testing framework  |        Version:  master
               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                  |
---------------------------------------------+------------------------
 Per [https://github.com/django/django/pull/8127#issuecomment-283322945
 Tim's suggestion], I've looked into a cleanup exception that happens when
 running tests on Python 3.6:
 {{{
 $ ./tests/runtests.py basic
 Testing against Django installed in '/home/tim/code/django/django' with up
 to 3 processes
 ....
 Destroying test database for alias 'other'...
  Traceback (most recent call last):
   File "/opt/python3.6.0/lib/python3.6/multiprocessing/util.py", line 254,
 in _run_finalizers
     finalizer()
   File "/opt/python3.6.0/lib/python3.6/multiprocessing/util.py", line 186,
 in __call__
     res = self._callback(*self._args, **self._kwargs)
   File "/home/tim/.virtualenvs/django36/lib/python3.6/shutil.py", line
 465, in rmtree
     onerror(os.lstat, path, sys.exc_info())
   File "/home/tim/.virtualenvs/django36/lib/python3.6/shutil.py", line
 463, in rmtree
     orig_st = os.lstat(path)
 FileNotFoundError: [Errno 2] No such file or directory:
 '/tmp/django_k0xziymh/pymp-i4s112bj'
 }}}

 What I found out:

 - `multiprocessing` is involved. Indeed, running with `--parallel=1` does
 not trigger this error.
 - Jinja2 is involved. Running tests without Jinja2 installed does not
 trigger this error.

 What happens:

 1. `multiprocessing` registers `atexit` handler.
 1. `runtests.py` creates a temp dir (e.g. `/tmp/django_k0xziymh`) and
 registers `atexit` handler to remove it.
 1. `multiprocessing` creates a temp dir (e.g. `/tmp/django_k0xziymh/pymp-
 i4s112bj`) which will be deleted in the handler it registered earlier.
 1. Tests happen.
 1. `runtests.py` exit handler deletes the temp dir.
 1. `multiprocessing` exit handler tries to delete the inner temp dir but
 it is already gone.

 On earlier Python versions 1 and 2 are swapped and 5 and 6 are swapped, so
 the error does not happen.

 Jinja2 is imported by a chain of imports starting with `from django.test
 import TestCase, TransactionTestCase` (full chain below).
 On Python 3.6 Jinja2
 [https://github.com/pallets/jinja/blob/master/jinja2/__init__.py#L74-L78
 patches async support] which eventually imports `multiprocessing.util`
 which registers the exit handler.

 Possible solutions:
 - Create the temp dir before importing `django` modules.
 - Move some imports in `runtests.py` into functions, thereby delaying the
 indirect import of Jinja2.
 - Move some imports somewhere else into functions to break the import
 chain.
 - ...

 The import chain leading up to the import of `multiprocessing.util` (with
 uninteresting import machinery stack frames removed):
 {{{
   File "./runtests.py", line 18, in <module>
     from django.test import TestCase, TransactionTestCase
   File "/home/vytis/src/django/django/test/__init__.py", line 5, in
 <module>
     from django.test.client import Client, RequestFactory
   File "/home/vytis/src/django/django/test/client.py", line 12, in
 <module>
     from django.core.handlers.base import BaseHandler
   File "/home/vytis/src/django/django/core/handlers/base.py", line 7, in
 <module>
     from django.urls import get_resolver, set_urlconf
   File "/home/vytis/src/django/django/urls/__init__.py", line 1, in
 <module>
     from .base import (
   File "/home/vytis/src/django/django/urls/base.py", line 8, in <module>
     from .exceptions import NoReverseMatch, Resolver404
   File "/home/vytis/src/django/django/urls/exceptions.py", line 1, in
 <module>
     from django.http import Http404
   File "/home/vytis/src/django/django/http/__init__.py", line 5, in
 <module>
     from django.http.response import (
   File "/home/vytis/src/django/django/http/response.py", line 13, in
 <module>
     from django.core.serializers.json import DjangoJSONEncoder
   File "/home/vytis/src/django/django/core/serializers/__init__.py", line
 23, in <module>
     from django.core.serializers.base import SerializerDoesNotExist
   File "/home/vytis/src/django/django/core/serializers/base.py", line 6,
 in <module>
     from django.db import models
   File "/home/vytis/src/django/django/db/models/__init__.py", line 3, in
 <module>
     from django.db.models.aggregates import *  # NOQA
   File "/home/vytis/src/django/django/db/models/aggregates.py", line 5, in
 <module>
     from django.db.models.expressions import Func, Star
   File "/home/vytis/src/django/django/db/models/expressions.py", line 6,
 in <module>
     from django.db.models import fields
   File "/home/vytis/src/django/django/db/models/fields/__init__.py", line
 11, in <module>
     from django import forms
   File "/home/vytis/src/django/django/forms/__init__.py", line 6, in
 <module>
     from django.forms.boundfield import *  # NOQA
   File "/home/vytis/src/django/django/forms/boundfield.py", line 5, in
 <module>
     from django.forms.widgets import Textarea, TextInput
   File "/home/vytis/src/django/django/forms/widgets.py", line 21, in
 <module>
     from .renderers import get_default_renderer
   File "/home/vytis/src/django/django/forms/renderers.py", line 11, in
 <module>
     from django.template.backends.jinja2 import Jinja2
   File "/home/vytis/src/django/django/template/backends/jinja2.py", line
 1, in <module>
     import jinja2
   File "/home/vytis/src/env/django-py36/lib/python3.6/site-
 packages/jinja2/__init__.py", line 81, in <module>
     _patch_async()
   File "/home/vytis/src/env/django-py36/lib/python3.6/site-
 packages/jinja2/__init__.py", line 77, in _patch_async
     from jinja2.asyncsupport import patch_all
   File "/home/vytis/src/env/django-py36/lib/python3.6/site-
 packages/jinja2/asyncsupport.py", line 13, in <module>
     import asyncio
   File "/opt/python/lib/python3.6/asyncio/__init__.py", line 21, in
 <module>
     from .base_events import *
   File "/opt/python/lib/python3.6/asyncio/base_events.py", line 17, in
 <module>
     import concurrent.futures
   File "/opt/python/lib/python3.6/concurrent/futures/__init__.py", line
 17, in <module>
     from concurrent.futures.process import ProcessPoolExecutor
   File "/opt/python/lib/python3.6/concurrent/futures/process.py", line 55,
 in <module>
     from multiprocessing.connection import wait
   File "/opt/python/lib/python3.6/multiprocessing/connection.py", line 23,
 in <module>
     from . import util
 }}}

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

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/049.2d87343e217ad30e741ed0dbf2345bb1%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to