#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 [email protected].
To post to this group, send email to [email protected].
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