The backported configparser is a P.I.T.A..
I tripped across this when trying to use py-jupyter-notebook with my
Spack PR that adds
dependencies via site.addsitedir(). It fails immediately. That package
appears to work if you use "normal" Spack and PYTHONPATH, but it turns
out that the only bit that uses configparser is jupyter-nbconvert and
that does fail.
TL;DR is that it fails quickly in PR #8436 because configparser's .pth file
is processed and breaks the backports namespace for every other package that
uses it. In normal Spack w/ PYTHONPATH configparser's .pth file is never
processed so the other backport packages work but when the code actually gets
around to trying to use configparser if fails.
In a standard Spack, with lmod modules...
Adding the directory to PYTHONPATH and trying to import it doesn't work:
(alice)[15:27:28]spack-plain>>module purge
(alice)[15:27:40]spack-plain>>module load py-configparser
(alice)[15:27:47]spack-plain>>python
Python 2.7.15 (default, Jun 19 2018, 12:32:41)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import configparser
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/hartzell/tmp/spack-plain/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-configparser-3.5.0-hcll7pv4hkl46st6vzswyrcedpq6r7vt/lib/python2.7/site-packages/configparser.py", line 12, in <module>
from backports.configparser import (
ImportError: No module named backports.configparser
>>> from backports import configparser
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named backports
>>>
pylint works in a Spack environment that's tying things together with
PYTHONPATH because when import configparser fails it just imports
from six.moves (which aliases it to the old-school ConfigParser)
Here's the relevant bit from pylint:
try:
import configparser
except ImportError:
from six.moves import configparserHere's the relevant bit from
six.moves.
For the record:
ConfigParseris the Python 2 thing.configparseris the Python 3 thing, in addition to the name change the functionality was improved (in 3.2?).- the
configparserpackage (which should be "backports-configparser"...) makes the updated version available to Python 2.
site.addsitedir()
provides an alternative to using PYTHONPATH to add directories to
sys.path. Its documentation says:
Add a directory to sys.path and process its .pth files. Typically used in sitecustomize or usercustomize (see above).
configparser installs a .pth file.
.../opt/spack/.../py-configparser-3.5.0-.../lib/python2.7/site-packages/configparser-3.5.0-py2.7-nspkg.pth
If configparser's directory is added to sys.path via
site.addsitedir() then it can be imported.
Here's an example of loading it:
(alice)[15:49:42]spack-plain>>module purge
(alice)[15:49:44]spack-plain>>module load py-configparser
(alice)[15:49:48]spack-plain>>printenv PYTHONPATH
/Users/hartzell/tmp/spack-plain/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-configparser-3.5.0-hcll7pv4hkl46st6vzswyrcedpq6r7vt/lib/python2.7/site-packages
(alice)[15:49:49]spack-plain>>module purge
(alice)[15:49:51]spack-plain>>python
Python 2.7.15 (default, May 1 2018, 16:44:08)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import site
>>> site.addsitedir("/Users/hartzell/tmp/spack-plain/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-configparser-3.5.0-hcll7pv4hkl46st6vzswyrcedpq6r7vt/lib/python2.7/site-packages")
>>> import configparser
>>>
I have a Spack PR that
writes a package's dependencies to a file and provides a
sitecustomize.py that reads that file when a script starts up and
loads the dirs via site.addsitedir(). It allows e.g. flake8 to
work without any environment variables.
Sadly, other packages don't work. E.g. py-jupyter-notebook.
It turns out that there are two different ways to create namespace packages in Python2 (plus a different one in Python3): pkgutil-style and pkg_resources-style.
You cannot mix packages that use the different techniques.
-
There's a
backportspackage that does nothing other than claim the namespace. It's pkgutil-style. -
Many (most?) "backports" packages, e.g. (backports-shutil-get-terminal-size) are pkgutil-style.
-
configparseris pkg_resources-style.
SIGH.
Here are some demos, using the system in my PR.
If you're not familiar with the PR, scripts figure out the directory
that they started in and look for a file named
../.spack/python-sitedirs. If it exists, they pass each line in it
to site.addsitedir.
When configparser's directory has been added via addsitedir, the
backports modules appears to be "built-in":
(alice)[16:13:13]bin>>cat ../.spack/python-sitedirs
/Users/hartzell/tmp/spack-rpaths/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-configparser-3.5.0-hcll7pv4hkl46st6vzswyrcedpq6r7vt
/Users/hartzell/tmp/spack-rpaths/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-backports-shutil-get-terminal-size-1.0.0-smyswe3gg4khpsinkiflyrhz3ra4zs5o
(alice)[16:17:20]bin>>cat foo
#!/bin/bash /Users/hartzell/tmp/spack-rpaths/bin/sbang
#!/Users/hartzell/tmp/spack-rpaths/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/python-2.7.15-fueghvuvuid6cw43s2eqovgyauoc73ii/bin/python2.7
import backports
print(backports)
(alice)[16:17:20]bin>>$(pwd)/foo
<module 'backports' (built-in)>
But if you don't add the configparser dir, then the backports
module points to the directory that contains it.
(alice)[16:13:13]bin>>cat ../.spack/python-sitedirs
/Users/hartzell/tmp/spack-rpaths/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-backports-shutil-get-terminal-size-1.0.0-smyswe3gg4khpsinkiflyrhz3ra4zs5o
(alice)[16:15:44]bin>>$(pwd)/foo
<module 'backports' from '/Users/hartzell/tmp/spack-rpaths/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-backports-shutil-get-terminal-size-1.0.0-smyswe3gg4khpsinkiflyrhz3ra4zs5o/lib/python2.7/site-packages/backports/__init__.pyc'>
Here's how that plays out with something that's actually trying to import a real piece of code.
(alice)[16:13:13]bin>>cat ../.spack/python-sitedirs
/Users/hartzell/tmp/spack-rpaths/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-configparser-3.5.0-hcll7pv4hkl46st6vzswyrcedpq6r7vt
/Users/hartzell/tmp/spack-rpaths/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-backports-shutil-get-terminal-size-1.0.0-smyswe3gg4khpsinkiflyrhz3ra4zs5o
(alice)[16:13:22]bin>>$(pwd)/demo
Traceback (most recent call last):
File "/Users/hartzell/tmp/spack-rpaths/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-jupyter-notebook-4.2.3-l4hlvjv7ce64xantfcnfiga5jnr546wr/bin/demo", line 5, in <module>
from backports.shutil_get_terminal_size import get_terminal_size as _get_terminal_size
ImportError: No module named shutil_get_terminal_size
(alice)[16:13:27]bin>># remove configparser line from ../.spack/python-sitedirs
(alice)[16:13:39]bin>>cat ../.spack/python-sitedirs
/Users/hartzell/tmp/spack-rpaths/opt/spack/darwin-highsierra-x86_64/clang-9.1.0-apple/py-backports-shutil-get-terminal-size-1.0.0-smyswe3gg4khpsinkiflyrhz3ra4zs5o
(alice)[16:13:43]bin>>$(pwd)/demo
(alice)[16:13:45]bin>>