Merge remote-tracking branch 'origin/issue590'

* origin/issue590:
  issue #590: disable distro test on vanilla
  ci: Ansible 2.8 jobs aren't running against all host types.
  tests: Py3.x fix.
  issue #590: fix test for <2.8 Ansibles.
  tests: Py3.x fix.
  issue #590: actually run Ansible test.
  tests: Py3.x fix.
  master: sysconfig did not exist until 2.7.
  tests: rearrange test modules again, they're used in multiple places
  module_finder_test: mask one more difference between unit2 vs. direct start
  master: fix _is_stdlib_path() failure on Ubuntu.
  issue #590: add dummy package for new test.
  issue #590: add FinderMethod docstrings.
  issue #590: update comment to indicate the hack is permanent
  issue #590: move example modules to module_finder/, fix/add tests
  issue #590: refactor ModuleFinder and teach it a new special case.
  issue #590: Ansible test for module_utils.distro use.
  issue #590: teach importer to handle self-replacing modules
pull/595/head
David Wilson 6 years ago
commit 92ac716f0d

@ -92,10 +92,8 @@ jobs:
python.version: '2.7' python.version: '2.7'
MODE: ansible MODE: ansible
VER: 2.8.0 VER: 2.8.0
DISTROS: debian
Ansible_280_35: Ansible_280_35:
python.version: '3.5' python.version: '3.5'
MODE: ansible MODE: ansible
VER: 2.8.0 VER: 2.8.0
DISTROS: debian

@ -763,7 +763,16 @@ class NewStyleRunner(ScriptRunner):
try: try:
mitogen.core.import_module(fullname) mitogen.core.import_module(fullname)
except ImportError: except ImportError:
# TODO: this is a huge hack to work around issue #590. # #590: Ansible 2.8 module_utils.distro is a package that
# replaces itself in sys.modules with a non-package during
# import. Prior to replacement, it is a real package containing
# a '_distro' submodule which is used on 2.x. Given a 2.x
# controller and 3.x target, the import hook never needs to run
# again before this replacement occurs, and 'distro' is
# replaced with a module from the stdlib. In this case as this
# loop progresses to the next entry and attempts to preload
# 'distro._distro', the import mechanism will fail. So here we
# silently ignore any failure for it.
if fullname != 'ansible.module_utils.distro._distro': if fullname != 'ansible.module_utils.distro._distro':
raise raise

@ -30,6 +30,12 @@ Enhancements
<https://docs.ansible.com/ansible/latest/plugins/become.html>`_ <https://docs.ansible.com/ansible/latest/plugins/become.html>`_
functionality, which will be addressed in a future release. functionality, which will be addressed in a future release.
Fixes
^^^^^
* `#590 <https://github.com/dw/mitogen/issues/590>`_: the importer can handle
modules that replace themselves in :mod:`sys.modules` during import.
Thanks! Thanks!
~~~~~~~ ~~~~~~~

@ -1355,7 +1355,10 @@ class Importer(object):
exec(code, vars(mod)) exec(code, vars(mod))
else: else:
exec('exec code in vars(mod)') exec('exec code in vars(mod)')
return mod
# #590: if a module replaces itself in sys.modules during import, below
# is necessary. This matches PyImport_ExecCodeModuleEx()
return sys.modules.get(fullname, mod)
def get_filename(self, fullname): def get_filename(self, fullname):
if fullname in self._cache: if fullname in self._cache:

@ -36,6 +36,7 @@ contexts.
""" """
import dis import dis
import errno
import imp import imp
import inspect import inspect
import itertools import itertools
@ -45,11 +46,16 @@ import pkgutil
import re import re
import string import string
import sys import sys
import time
import threading import threading
import time
import types import types
import zlib import zlib
try:
import sysconfig
except ImportError:
sysconfig = None
if not hasattr(pkgutil, 'find_loader'): if not hasattr(pkgutil, 'find_loader'):
# find_loader() was new in >=2.5, but the modern pkgutil.py syntax has # find_loader() was new in >=2.5, but the modern pkgutil.py syntax has
# been kept intentionally 2.3 compatible so we can reuse it. # been kept intentionally 2.3 compatible so we can reuse it.
@ -92,10 +98,16 @@ def _stdlib_paths():
'real_prefix', # virtualenv: only set inside a virtual environment. 'real_prefix', # virtualenv: only set inside a virtual environment.
'base_prefix', # venv: always set, equal to prefix if outside. 'base_prefix', # venv: always set, equal to prefix if outside.
] ]
prefixes = (getattr(sys, a) for a in attr_candidates if hasattr(sys, a)) prefixes = (getattr(sys, a, None) for a in attr_candidates)
version = 'python%s.%s' % sys.version_info[0:2] version = 'python%s.%s' % sys.version_info[0:2]
return set(os.path.abspath(os.path.join(p, 'lib', version)) s = set(os.path.abspath(os.path.join(p, 'lib', version))
for p in prefixes) for p in prefixes if p is not None)
# When running 'unit2 tests/module_finder_test.py' in a Py2 venv on Ubuntu
# 18.10, above is insufficient to catch the real directory.
if sysconfig is not None:
s.add(sysconfig.get_config_var('DESTLIB'))
return s
def is_stdlib_name(modname): def is_stdlib_name(modname):
@ -142,6 +154,41 @@ def get_child_modules(path):
return [to_text(name) for _, name, _ in it] return [to_text(name) for _, name, _ in it]
def _looks_like_script(path):
"""
Return :data:`True` if the (possibly extensionless) file at `path`
resembles a Python script. For now we simply verify the file contains
ASCII text.
"""
try:
fp = open(path, 'rb')
except IOError:
e = sys.exc_info()[1]
if e.args[0] == errno.EISDIR:
return False
raise
try:
sample = fp.read(512).decode('latin-1')
return not set(sample).difference(string.printable)
finally:
fp.close()
def _py_filename(path):
if not path:
return None
if path[-4:] in ('.pyc', '.pyo'):
path = path.rstrip('co')
if path.endswith('.py'):
return path
if os.path.exists(path) and _looks_like_script(path):
return path
def _get_core_source(): def _get_core_source():
""" """
Master version of parent.get_core_source(). Master version of parent.get_core_source().
@ -368,56 +415,38 @@ class LogForwarder(object):
return 'LogForwarder(%r)' % (self._router,) return 'LogForwarder(%r)' % (self._router,)
class ModuleFinder(object): class FinderMethod(object):
""" """
Given the name of a loaded module, make a best-effort attempt at finding Interface to a method for locating a Python module or package given its
related modules likely needed by a child context requesting the original name according to the running Python interpreter. You'd think this was a
module. simple task, right? Naive young fellow, welcome to the real world.
""" """
def __init__(self):
#: Import machinery is expensive, keep :py:meth`:get_module_source`
#: results around.
self._found_cache = {}
#: Avoid repeated dependency scanning, which is expensive.
self._related_cache = {}
def __repr__(self): def __repr__(self):
return 'ModuleFinder()' return '%s()' % (type(self).__name__,)
def _looks_like_script(self, path): def find(self, fullname):
"""
Return :data:`True` if the (possibly extensionless) file at `path`
resembles a Python script. For now we simply verify the file contains
ASCII text.
""" """
fp = open(path, 'rb') Accept a canonical module name and return `(path, source, is_pkg)`
try: tuples, where:
sample = fp.read(512).decode('latin-1')
return not set(sample).difference(string.printable)
finally:
fp.close()
def _py_filename(self, path): * `path`: Unicode string containing path to source file.
if not path: * `source`: Bytestring containing source file's content.
return None * `is_pkg`: :data:`True` if `fullname` is a package.
if path[-4:] in ('.pyc', '.pyo'): :returns:
path = path.rstrip('co') :data:`None` if not found, or tuple as described above.
"""
if path.endswith('.py'): raise NotImplementedError()
return path
if os.path.exists(path) and self._looks_like_script(path):
return path
def _get_main_module_defective_python_3x(self, fullname): class DefectivePython3xMainMethod(FinderMethod):
""" """
Recent versions of Python 3.x introduced an incomplete notion of Recent versions of Python 3.x introduced an incomplete notion of
importer specs, and in doing so created permanent asymmetry in the importer specs, and in doing so created permanent asymmetry in the
:mod:`pkgutil` interface handling for the `__main__` module. Therefore :mod:`pkgutil` interface handling for the `__main__` module. Therefore
we must handle `__main__` specially. we must handle `__main__` specially.
""" """
def find(self, fullname):
if fullname != '__main__': if fullname != '__main__':
return None return None
@ -426,7 +455,7 @@ class ModuleFinder(object):
return None return None
path = getattr(mod, '__file__', None) path = getattr(mod, '__file__', None)
if not (os.path.exists(path) and self._looks_like_script(path)): if not (os.path.exists(path) and _looks_like_script(path)):
return None return None
fp = open(path, 'rb') fp = open(path, 'rb')
@ -437,11 +466,13 @@ class ModuleFinder(object):
return path, source, False return path, source, False
def _get_module_via_pkgutil(self, fullname):
class PkgutilMethod(FinderMethod):
""" """
Attempt to fetch source code via pkgutil. In an ideal world, this would Attempt to fetch source code via pkgutil. In an ideal world, this would
be the only required implementation of get_module(). be the only required implementation of get_module().
""" """
def find(self, fullname):
try: try:
# Pre-'import spec' this returned None, in Python3.6 it raises # Pre-'import spec' this returned None, in Python3.6 it raises
# ImportError. # ImportError.
@ -458,7 +489,7 @@ class ModuleFinder(object):
return return
try: try:
path = self._py_filename(loader.get_filename(fullname)) path = _py_filename(loader.get_filename(fullname))
source = loader.get_source(fullname) source = loader.get_source(fullname)
is_pkg = loader.is_package(fullname) is_pkg = loader.is_package(fullname)
except (AttributeError, ImportError): except (AttributeError, ImportError):
@ -484,19 +515,27 @@ class ModuleFinder(object):
return path, source, is_pkg return path, source, is_pkg
def _get_module_via_sys_modules(self, fullname):
class SysModulesMethod(FinderMethod):
""" """
Attempt to fetch source code via sys.modules. This is specifically to Attempt to fetch source code via sys.modules. This is specifically to
support __main__, but it may catch a few more cases. support __main__, but it may catch a few more cases.
""" """
def find(self, fullname):
module = sys.modules.get(fullname) module = sys.modules.get(fullname)
LOG.debug('_get_module_via_sys_modules(%r) -> %r', fullname, module) LOG.debug('_get_module_via_sys_modules(%r) -> %r', fullname, module)
if getattr(module, '__name__', None) != fullname:
LOG.debug('sys.modules[%r].__name__ does not match %r, assuming '
'this is a hacky module alias and ignoring it',
fullname, fullname)
return
if not isinstance(module, types.ModuleType): if not isinstance(module, types.ModuleType):
LOG.debug('sys.modules[%r] absent or not a regular module', LOG.debug('sys.modules[%r] absent or not a regular module',
fullname) fullname)
return return
path = self._py_filename(getattr(module, '__file__', '')) path = _py_filename(getattr(module, '__file__', ''))
if not path: if not path:
return return
@ -517,12 +556,19 @@ class ModuleFinder(object):
return path, source, is_pkg return path, source, is_pkg
def _get_module_via_parent_enumeration(self, fullname):
class ParentEnumerationMethod(FinderMethod):
""" """
Attempt to fetch source code by examining the module's (hopefully less Attempt to fetch source code by examining the module's (hopefully less
insane) parent package. Required for older versions of insane) parent package. Required for older versions of
ansible.compat.six and plumbum.colors. ansible.compat.six and plumbum.colors, and Ansible 2.8
ansible.module_utils.distro.
For cases like module_utils.distro, this must handle cases where a package
transmuted itself into a totally unrelated module during import and vice
versa.
""" """
def find(self, fullname):
if fullname not in sys.modules: if fullname not in sys.modules:
# Don't attempt this unless a module really exists in sys.modules, # Don't attempt this unless a module really exists in sys.modules,
# else we could return junk. # else we could return junk.
@ -531,15 +577,38 @@ class ModuleFinder(object):
pkgname, _, modname = str_rpartition(to_text(fullname), u'.') pkgname, _, modname = str_rpartition(to_text(fullname), u'.')
pkg = sys.modules.get(pkgname) pkg = sys.modules.get(pkgname)
if pkg is None or not hasattr(pkg, '__file__'): if pkg is None or not hasattr(pkg, '__file__'):
LOG.debug('%r: %r is not a package or lacks __file__ attribute',
self, pkgname)
return return
pkg_path = os.path.dirname(pkg.__file__) pkg_path = [os.path.dirname(pkg.__file__)]
try: try:
fp, path, ext = imp.find_module(modname, [pkg_path]) fp, path, (suffix, _, kind) = imp.find_module(modname, pkg_path)
except ImportError:
e = sys.exc_info()[1]
LOG.debug('%r: imp.find_module(%r, %r) -> %s',
self, modname, [pkg_path], e)
return None
if kind == imp.PKG_DIRECTORY:
return self._found_package(fullname, path)
else:
return self._found_module(fullname, path, fp)
def _found_package(self, fullname, path):
path = os.path.join(path, '__init__.py')
LOG.debug('%r: %r is PKG_DIRECTORY: %r', self, fullname, path)
return self._found_module(
fullname=fullname,
path=path,
fp=open(path, 'rb'),
is_pkg=True,
)
def _found_module(self, fullname, path, fp, is_pkg=False):
try: try:
path = self._py_filename(path) path = _py_filename(path)
if not path: if not path:
fp.close()
return return
source = fp.read() source = fp.read()
@ -551,10 +620,25 @@ class ModuleFinder(object):
# get_source() returns "string" according to PEP-302, which was # get_source() returns "string" according to PEP-302, which was
# reinterpreted for Python 3 to mean a Unicode string. # reinterpreted for Python 3 to mean a Unicode string.
source = source.encode('utf-8') source = source.encode('utf-8')
return path, source, False return path, source, is_pkg
except ImportError:
e = sys.exc_info()[1]
LOG.debug('imp.find_module(%r, %r) -> %s', modname, [pkg_path], e) class ModuleFinder(object):
"""
Given the name of a loaded module, make a best-effort attempt at finding
related modules likely needed by a child context requesting the original
module.
"""
def __init__(self):
#: Import machinery is expensive, keep :py:meth`:get_module_source`
#: results around.
self._found_cache = {}
#: Avoid repeated dependency scanning, which is expensive.
self._related_cache = {}
def __repr__(self):
return 'ModuleFinder()'
def add_source_override(self, fullname, path, source, is_pkg): def add_source_override(self, fullname, path, source, is_pkg):
""" """
@ -576,10 +660,10 @@ class ModuleFinder(object):
self._found_cache[fullname] = (path, source, is_pkg) self._found_cache[fullname] = (path, source, is_pkg)
get_module_methods = [ get_module_methods = [
_get_main_module_defective_python_3x, DefectivePython3xMainMethod(),
_get_module_via_pkgutil, PkgutilMethod(),
_get_module_via_sys_modules, SysModulesMethod(),
_get_module_via_parent_enumeration, ParentEnumerationMethod(),
] ]
def get_module_source(self, fullname): def get_module_source(self, fullname):
@ -595,7 +679,7 @@ class ModuleFinder(object):
return tup return tup
for method in self.get_module_methods: for method in self.get_module_methods:
tup = method(self, fullname) tup = method.find(fullname)
if tup: if tup:
#LOG.debug('%r returned %r', method, tup) #LOG.debug('%r returned %r', method, tup)
break break

@ -0,0 +1,21 @@
#!/usr/bin/python
# issue #590: I am an Ansible new-style Python module that tries to use
# ansible.module_utils.distro.
import ansible
from ansible.module_utils.basic import AnsibleModule
if ansible.__version__ > '2.8':
from ansible.module_utils import distro
else:
distro = None
def main():
module = AnsibleModule(argument_spec={})
if ansible.__version__ > '2.8':
module.exit_json(info=distro.info())
else:
module.exit_json(info={'id': None})
if __name__ == '__main__':
main()

@ -8,3 +8,4 @@
- include: issue_154__module_state_leaks.yml - include: issue_154__module_state_leaks.yml
- include: issue_177__copy_module_failing.yml - include: issue_177__copy_module_failing.yml
- include: issue_332_ansiblemoduleerror_first_occurrence.yml - include: issue_332_ansiblemoduleerror_first_occurrence.yml
- include: issue_590__sys_modules_crap.yml

@ -0,0 +1,12 @@
- hosts: test-targets
tasks:
- meta: end_play
when: ansible_version.full < '2.8'
- custom_python_uses_distro:
register: out
- assert:
that:
- "'id' in out.info"

@ -0,0 +1,5 @@
# #590: a package that turns itself into a module.
I_AM = "the package that was replaced"
import sys
from pkg_like_ansible.module_utils.distro import _distro
sys.modules[__name__] = _distro

@ -0,0 +1 @@
I_AM = "the module that replaced the package"

@ -0,0 +1,6 @@
# issue #590: this module imports a module that replaces itself in sys.modules
# during initialization.
import simple_pkg.replaces_self
def subtract_one(n):
return simple_pkg.replaces_self.subtract_one(n)

@ -0,0 +1,4 @@
# issue #590: this module replaces itself in sys.modules during initialization.
import sys
import simple_pkg.b
sys.modules[__name__] = simple_pkg.b

@ -12,6 +12,7 @@ import mitogen.utils
from mitogen.core import b from mitogen.core import b
import testlib import testlib
import simple_pkg.imports_replaces_self
class ImporterMixin(testlib.RouterMixin): class ImporterMixin(testlib.RouterMixin):
@ -214,5 +215,13 @@ class Python24LineCacheTest(testlib.TestCase):
pass pass
class SelfReplacingModuleTest(testlib.RouterMixin, testlib.TestCase):
# issue #590
def test_importer_handles_self_replacement(self):
c = self.router.local()
self.assertEquals(0,
c.call(simple_pkg.imports_replaces_self.subtract_one, 1))
if __name__ == '__main__': if __name__ == '__main__':
unittest2.main() unittest2.main()

@ -5,8 +5,10 @@ import sys
import unittest2 import unittest2
import mitogen.master import mitogen.master
from mitogen.core import b
import testlib import testlib
from testlib import MODS_DIR
class ConstructorTest(testlib.TestCase): class ConstructorTest(testlib.TestCase):
@ -51,10 +53,10 @@ class IsStdlibNameTest(testlib.TestCase):
class GetMainModuleDefectivePython3x(testlib.TestCase): class GetMainModuleDefectivePython3x(testlib.TestCase):
klass = mitogen.master.ModuleFinder klass = mitogen.master.DefectivePython3xMainMethod
def call(self, fullname): def call(self, fullname):
return self.klass()._get_main_module_defective_python_3x(fullname) return self.klass().find(fullname)
def test_builtin(self): def test_builtin(self):
self.assertEquals(None, self.call('sys')) self.assertEquals(None, self.call('sys'))
@ -77,23 +79,23 @@ class GetMainModuleDefectivePython3x(testlib.TestCase):
self.assertFalse(is_pkg) self.assertFalse(is_pkg)
class GetModuleViaPkgutilTest(testlib.TestCase): class PkgutilMethodTest(testlib.TestCase):
klass = mitogen.master.ModuleFinder klass = mitogen.master.PkgutilMethod
def call(self, fullname): def call(self, fullname):
return self.klass()._get_module_via_pkgutil(fullname) return self.klass().find(fullname)
def test_empty_source_pkg(self): def test_empty_source_pkg(self):
path, src, is_pkg = self.call('module_finder_testmod') path, src, is_pkg = self.call('module_finder_testmod')
self.assertEquals(path, self.assertEquals(path,
testlib.data_path('module_finder_testmod/__init__.py')) os.path.join(MODS_DIR, 'module_finder_testmod/__init__.py'))
self.assertEquals(mitogen.core.b(''), src) self.assertEquals(mitogen.core.b(''), src)
self.assertTrue(is_pkg) self.assertTrue(is_pkg)
def test_empty_source_module(self): def test_empty_source_module(self):
path, src, is_pkg = self.call('module_finder_testmod.empty_mod') path, src, is_pkg = self.call('module_finder_testmod.empty_mod')
self.assertEquals(path, self.assertEquals(path,
testlib.data_path('module_finder_testmod/empty_mod.py')) os.path.join(MODS_DIR, 'module_finder_testmod/empty_mod.py'))
self.assertEquals(mitogen.core.b(''), src) self.assertEquals(mitogen.core.b(''), src)
self.assertFalse(is_pkg) self.assertFalse(is_pkg)
@ -101,23 +103,29 @@ class GetModuleViaPkgutilTest(testlib.TestCase):
from module_finder_testmod import regular_mod from module_finder_testmod import regular_mod
path, src, is_pkg = self.call('module_finder_testmod.regular_mod') path, src, is_pkg = self.call('module_finder_testmod.regular_mod')
self.assertEquals(path, self.assertEquals(path,
testlib.data_path('module_finder_testmod/regular_mod.py')) os.path.join(MODS_DIR, 'module_finder_testmod/regular_mod.py'))
self.assertEquals(mitogen.core.to_text(src), self.assertEquals(mitogen.core.to_text(src),
inspect.getsource(regular_mod)) inspect.getsource(regular_mod))
self.assertFalse(is_pkg) self.assertFalse(is_pkg)
class GetModuleViaSysModulesTest(testlib.TestCase): class SysModulesMethodTest(testlib.TestCase):
klass = mitogen.master.ModuleFinder klass = mitogen.master.SysModulesMethod
def call(self, fullname): def call(self, fullname):
return self.klass()._get_module_via_sys_modules(fullname) return self.klass().find(fullname)
def test_main(self): def test_main(self):
import __main__ import __main__
path, src, is_pkg = self.call('__main__') path, src, is_pkg = self.call('__main__')
self.assertEquals(path, __main__.__file__) self.assertEquals(path, __main__.__file__)
self.assertEquals(src, open(path, 'rb').read())
# linecache adds a line ending to the final line if one is missing.
actual_src = open(path, 'rb').read()
if actual_src[-1:] != b('\n'):
actual_src += b('\n')
self.assertEquals(src, actual_src)
self.assertFalse(is_pkg) self.assertFalse(is_pkg)
def test_dylib_fails(self): def test_dylib_fails(self):
@ -133,10 +141,10 @@ class GetModuleViaSysModulesTest(testlib.TestCase):
class GetModuleViaParentEnumerationTest(testlib.TestCase): class GetModuleViaParentEnumerationTest(testlib.TestCase):
klass = mitogen.master.ModuleFinder klass = mitogen.master.ParentEnumerationMethod
def call(self, fullname): def call(self, fullname):
return self.klass()._get_module_via_parent_enumeration(fullname) return self.klass().find(fullname)
def test_main_fails(self): def test_main_fails(self):
import __main__ import __main__
@ -157,13 +165,28 @@ class GetModuleViaParentEnumerationTest(testlib.TestCase):
# plumbum has been eating too many rainbow-colored pills # plumbum has been eating too many rainbow-colored pills
import pkg_like_plumbum.colors import pkg_like_plumbum.colors
path, src, is_pkg = self.call('pkg_like_plumbum.colors') path, src, is_pkg = self.call('pkg_like_plumbum.colors')
self.assertEquals(path, modpath = os.path.join(MODS_DIR, 'pkg_like_plumbum/colors.py')
testlib.data_path('pkg_like_plumbum/colors.py')) self.assertEquals(path, modpath)
s = open(testlib.data_path('pkg_like_plumbum/colors.py'), 'rb').read() self.assertEquals(src, open(modpath, 'rb').read())
self.assertEquals(src, s)
self.assertFalse(is_pkg) self.assertFalse(is_pkg)
def test_ansible_module_utils_distro_succeeds(self):
# #590: a package that turns itself into a module.
import pkg_like_ansible.module_utils.distro as d
self.assertEquals(d.I_AM, "the module that replaced the package")
self.assertEquals(
sys.modules['pkg_like_ansible.module_utils.distro'].__name__,
'pkg_like_ansible.module_utils.distro._distro'
)
path, src, is_pkg = self.call('pkg_like_ansible.module_utils.distro')
modpath = os.path.join(MODS_DIR,
'pkg_like_ansible/module_utils/distro/__init__.py')
self.assertEquals(path, modpath)
self.assertEquals(src, open(modpath, 'rb').read())
self.assertEquals(is_pkg, True)
class ResolveRelPathTest(testlib.TestCase): class ResolveRelPathTest(testlib.TestCase):
klass = mitogen.master.ModuleFinder klass = mitogen.master.ModuleFinder
@ -235,7 +258,7 @@ class FindRelatedTest(testlib.TestCase):
if sys.version_info > (2, 6): if sys.version_info > (2, 6):
class DjangoMixin(object): class DjangoMixin(object):
WEBPROJECT_PATH = testlib.data_path('webproject') WEBPROJECT_PATH = os.path.join(MODS_DIR, 'webproject')
# TODO: rip out Django and replace with a static tree of weird imports # TODO: rip out Django and replace with a static tree of weird imports
# that don't depend on .. Django! The hack below is because the version # that don't depend on .. Django! The hack below is because the version

@ -41,7 +41,11 @@ except NameError:
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
DATA_DIR = os.path.join(os.path.dirname(__file__), 'data') DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
MODS_DIR = os.path.join(DATA_DIR, 'importer')
sys.path.append(DATA_DIR) sys.path.append(DATA_DIR)
sys.path.append(MODS_DIR)
if mitogen.is_master: if mitogen.is_master:
mitogen.utils.log_to_file() mitogen.utils.log_to_file()

Loading…
Cancel
Save