diff --git a/docs/shame.rst b/docs/shame.rst new file mode 100644 index 00000000..10d4d38e --- /dev/null +++ b/docs/shame.rst @@ -0,0 +1,44 @@ + +Module Import Wall Of Shame +--------------------------- + +The following modules and packages run magic during ``__init.py__`` that makes +life hard for Mitogen. Executing code during module import is always bad, and +Mitogen is a concrete benchmark for why it's bad. + + +``pkg_resources`` +================= + +Anything that imports ``pkg_resources`` will eventually cause +pkg_resources to try and import and scan ``__main__`` for its ``__requires__`` +attribute (``pkg_resources/__init__.py::_build_master()``). This breaks any app +that is not expecting its ``__main__`` to suddenly be sucked over a network and +injected into a remote process, like py.test. + +A future version of Mitogen might have a more general hack that doesn't import +the master's ``__main__`` as ``__main__`` in the slave, avoiding all kinds of +issues like these. + +**What could it do instead?** + +* Explicit is better than implicit: wait until the magical behaviour is + explicitly requested (i.e. an API call). + +* Use ``get("__main__")`` on :py:attr:`sys.modules` rather than ``import``, but + this method isn't general enough, it only really helps tools like Mitogen. + + +``pbr`` +======= + +It claims to use ``pkg_resources`` to read version information +(``_get_version_from_pkg_metadata()``), which would result in PEP-302 being +reused and everything just working wonderfully, but instead it actually does +direct filesystem access. So we smodge the environment with a ``PBR_VERSION`` +environment variable to override any version that was defined. This will +probably break code I haven't seen yet. + +**What could it do instead?** + +* ``pkg_resources.get_resource_stream()`` diff --git a/docs/toc.rst b/docs/toc.rst index 8b173669..ac62ee4a 100644 --- a/docs/toc.rst +++ b/docs/toc.rst @@ -11,3 +11,4 @@ Table Of Contents getting_started api internals + shame diff --git a/mitogen/core.py b/mitogen/core.py index 24054ebf..3699d013 100644 --- a/mitogen/core.py +++ b/mitogen/core.py @@ -368,8 +368,31 @@ class Importer(object): finally: del self.tls.running + def _load_module_hacks(self, fullname): + f = sys._getframe(2) + requestee = f.f_globals['__name__'] + + if fullname == '__main__' and requestee == 'pkg_resources': + # Anything that imports pkg_resources will eventually cause + # pkg_resources to try and scan __main__ for its __requires__ + # attribute (pkg_resources/__init__.py::_build_master()). This + # breaks any app that is not expecting its __main__ to suddenly be + # sucked over a network and injected into a remote process, like + # py.test. + raise ImportError('Refused') + + if fullname == 'pbr': + # It claims to use pkg_resources to read version information, which + # would result in PEP-302 being used, but it actually does direct + # filesystem access. So instead smodge the environment to override + # any version that was defined. This will probably break something + # later. + os.environ['PBR_VERSION'] = '0.0.0' + def load_module(self, fullname): LOG.debug('Importer.load_module(%r)', fullname) + self._load_module_hacks(fullname) + try: ret = self._cache[fullname] except KeyError: