Repository: pex-tool/pex

Issue:
version 1.6.9 fails with null module __file__ attribute
On my arch linux system, after the pants upgrade to version 1.6.9, a number of pieces of pants functionality that make use of pex started failing with the following error message:

```
                     Traceback (most recent call last):
                       File "/home/gregs/code/pants/.pants.d/test/pytest-prep/CPython-3.7.3/7c81bb6ec656c4064b548c392d519f0695716e81/.bootstrap/pex/pex.py", line 356, in execute
                         self.patch_sys(pex_inherit_path)
                       File "/home/gregs/code/pants/.pants.d/test/pytest-prep/CPython-3.7.3/7c81bb6ec656c4064b548c392d519f0695716e81/.bootstrap/pex/pex.py", line 285, in patch_sys
                         new_sys_path, new_sys_path_importer_cache, new_sys_modules = self.minimum_sys(inherit_path)
                       File "/home/gregs/code/pants/.pants.d/test/pytest-prep/CPython-3.7.3/7c81bb6ec656c4064b548c392d519f0695716e81/.bootstrap/pex/pex.py", line 259, in minimum_sys
                         sys_modules = cls.minimum_sys_modules(site_libs)
                       File "/home/gregs/code/pants/.pants.d/test/pytest-prep/CPython-3.7.3/7c81bb6ec656c4064b548c392d519f0695716e81/.bootstrap/pex/pex.py", line 179, in minimum_sys_modules
                         if cls._tainted_path(module_file, site_libs):
                       File "/home/gregs/code/pants/.pants.d/test/pytest-prep/CPython-3.7.3/7c81bb6ec656c4064b548c392d519f0695716e81/.bootstrap/pex/pex.py", line 157, in _tainted_path
                         paths = frozenset([path, os.path.realpath(path)])
                       File "/home/gregs/code/pants/build-support/pants_dev_deps.py37.venv/lib/python3.7/posixpath.py", line 394, in realpath
                         filename = os.fspath(filename)
                     TypeError: expected str, bytes or os.PathLike object, not NoneType
```
It looks like what's going on here is that https://github.com/pantsbuild/pex/blob/master/pex/pex.py#L178 is iterating over every module and extracting the `__file__` attribute of that mdoule. However, some module on my system seems to have a `None` value for that attribute, which is causing code downstream of that line to fail. It looks like the commit https://github.com/pantsbuild/pex/pull/752/commits/b5f7e41780c8f05a07640dc09fdeed67e1b7a6a6 might be relevant.


Discussion:

Resolving patch:
diff --git a/pex/pex.py b/pex/pex.py
--- a/pex/pex.py
+++ b/pex/pex.py
@@ -175,8 +175,8 @@ def minimum_sys_modules(cls, site_libs, modules=None):
 
     for module_name, module in modules.items():
       # Tainted modules should be dropped.
-      module_file = getattr(module, '__file__', os.devnull)
-      if cls._tainted_path(module_file, site_libs):
+      module_file = getattr(module, '__file__', None)
+      if module_file and cls._tainted_path(module_file, site_libs):
         TRACER.log('Dropping %s' % (module_name,), V=3)
         continue
 


Test patch:
diff --git a/tests/test_pex.py b/tests/test_pex.py
--- a/tests/test_pex.py
+++ b/tests/test_pex.py
@@ -173,6 +173,13 @@ class FakeModule(object):
   new_modules = PEX.minimum_sys_modules(site_libs=['bad_path'], modules=modules)
   assert new_modules == {}
 
+  # If __file__ is explicitly None we should gracefully proceed to __path__ checks.
+  tainted_module = ModuleType('tainted_module')
+  tainted_module.__file__ = None
+  modules = {'tainted_module': tainted_module}
+  new_modules = PEX.minimum_sys_modules(site_libs=[], modules=modules)
+  assert new_modules == modules
+
 
 def test_site_libs():
   with nested(mock.patch.object(PEX, '_get_site_packages'), temporary_dir()) as (