Why does Python raise ModuleNotFoundError when modifying Django's INSTALLED_APPS?2020-06-29
Imagine we are installing the third party package django-cors-headers, which I maintain. Step one in its installation process is to install the package, so we run the command:
Step two is to add the module to our settings file’s
We might add it between the the Django contrib apps and our project’s own
Unfortunately with the above
INSTALLED_APPS has a bug.
Can you spot it?
It causes Python to raise the following
ModuleNotFoundError when trying to run the server:
The mistake is that a comma is missing at the end of
This causes Python to glue the two app module strings together -
"example.core", leading to
(This is a feature known as implicit string concatenation - more on which below.)
Django sees only
"corsheadersexample.core" in the
Thus when it starts up, it tries to import a module by that name.
Python resolves that by first trying to import
"corsheadersexample", which does not exist.
All the frames in our traceback that appear in
"<frozen importlib._bootstrap>" are Python’s built-in import machinery.
The solution is a small change - to add a comma after
We can also add a comma after
"example.core", to protect against this mistake happening again if we add an app at the end:
Then Django should be able to import both
"example.core" and start.
And we can continue following the rest of the installation steps for django-cors-headers.
Implicit String Concatenation
This automatic concatenation of strings, even on separate lines or with separate quoting styles, is a Python feature. It’s documented in String Literal Concatenation in the “Lexical analysis” section of the language reference.
The feature is controversial.
It makes writing long strings easier, as the
+ operator isn’t needed between the pieces.
However it leads to bugs like this, where two strings were meant instead of one.
PEP 3126 suggested removing this feature from Python during the transition from Python 2 to 3, however it was rejected.
More recently, Guido van Rossum, the creator of Python, suggested its removal on the python-ideas mailing list. Whilst a mixed conversation, it seems the removal of this feature would be generally accepted, but it’s hard to do so because of backwards compatibility concerns. Therefore, it’s unlikely to be removed any time soon.
I personally tend to use explicit concatenation, writing out the plus signs, whenever I split a string. I also try to always end lines in multi-line function calls and data structures with a comma. Then I know any implicit string concatenation I spot is my mistake.
Hope this helps,
Are your Django project's tests slow? Read Speed Up Your Django Tests now!
One summary email a week, no spam, I pinky promise.
- Introducing time-machine, a New Python Library for Mocking the Current Time
- How to Combine Two Python Decorators
- Use 'python -m pip' Everywhere
© 2020 All rights reserved.