Opened 9 years ago

Closed 4 years ago

#24559 closed Bug (fixed)

MigrationLoader.load_disk ImportError handling differs under py3/pypy vs py2

Reported by: Keryn Knight Owned by: Jon Dufresne
Component: Migrations Version: dev
Severity: Normal Keywords:
Cc: django@… Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Currently, within load_disk, the following happens:

            try:
                module = import_module(module_name)
            except ImportError as e:
                # I hate doing this, but I don't want to squash other import errors.
                # Might be better to try a directory check directly.
                if "No module named" in str(e) and MIGRATIONS_MODULE_NAME in str(e):
                    self.unmigrated_apps.add(app_config.label)
                    continue
                raise

However, the if statement behaves differently under pypy & python3, because the exception string is different.

Under python2, the following yields

>>> from importlib import import_module
>>> importlib('test_auth.migrations')
ImportError: No module named test_auth.migrations

which would hit both parts of the conditional, and end up in self.unmigrated_apps

However, under python3.3.3 the exception is:

ImportError: No module named 'test_auth'

and under pypy 2.2.1 (for python 2.7.3) the exception is:

ImportError: No module named test_auth

(same as py3k, sans quotes)

Neither of these would end up self.unmigrated_apps because MIGRATIONS_MODULE_NAME doesn't appear in the string representation, though I suspect import_module('testauthmigrations') would work.

I originally noticed this because of a travis build for a toy project, so it may not be a problem in practice, but it is at least inconsistent.

Change History (4)

comment:1 by Tim Graham, 9 years ago

Triage Stage: UnreviewedAccepted

comment:2 by Jon Dufresne, 4 years ago

Has patch: set

https://github.com/django/django/pull/12756

Python 2 is no longer suported, but this bug report shows why inspecting an exception's message isn't a stable API. The above PR changes the code to use the exception type and its "name" property.

comment:3 by Mariusz Felisiak, 4 years ago

Owner: changed from nobody to Jon Dufresne
Status: newassigned
Triage Stage: AcceptedReady for checkin

comment:4 by Mariusz Felisiak <felisiak.mariusz@…>, 4 years ago

Resolution: fixed
Status: assignedclosed

In 661e39c8:

Fixed #24559 -- Made MigrationLoader.load_disk() catch more specific ModuleNotFoundError.

Avoids inspecting the exception message, which is not considered a
stable API and can change across Python versions.

ModuleNotFoundError was introduced in Python 3.6. It is a subclass of
ImportError that is raised when the imported module does not exist. It
is not raised for other errors that can occur during an import. This
exception instance has the property "name" which holds the name of
module that failed to import.

Note: See TracTickets for help on using tickets.
Back to Top