路径中的Python模块:在mako模板中引发ImportError

2 投票
1 回答
2197 浏览
提问于 2025-04-16 22:14

我遇到了一个让人抓狂的导入错误。情况是这样的:

tests/
    testWebsite.py
website/
    __init__.py
    __main__.py
    _webtools/
        __init__.py
        templatedefs.py
        ...
    _templates/
        base.mako
        article.mako
        ...

这段代码(不包括测试目录,因为在问题解决之前我不太想提交)可以在这里找到:https://github.com/Boldewyn/website/

当我运行 python -m website.__main__ build 时,主程序会根据一些输入,使用 website/_templates 下的模板生成静态的 HTML 文件。这在任何目录下都能正常工作。

但是,在 tests/testWebsite.py 中,我有一个单元测试,也应该运行同样的功能。但在这里,Mako 模板却出现了导入错误,而在其他情况下这些文件的导入都没有问题。

$ head -n 5 website/_templates/article.mako
# -*- coding: utf-8 -*-
<%!
from website._webtools.templatedefs import strip_tags
%>
<%inherit file="base.mako" />

运行测试后,结果是:

$ python -m unittest tests.testWebsite
...
ERROR: test_initial_build (tests.testWebsite.BuildTestCase)
Check, if building directly after bootstrap works
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/testWebsite.py", line 99, in test_initial_build
  File "website/_webtools/build.py", line 89, in build
    article.save(articles=articles)
  File "website/_webtools/articles.py", line 514, in save
    template_engine.render_article(self, **ctx)
  File "website/_webtools/templates.py", line 52, in render_article
    r.render_article(article, **ctx)
  File "website/_webtools/templates.py", line 277, in render_article
    tpl = self.lookup.get_template(filename)
  File "/usr/lib/python2.7/dist-packages/mako/lookup.py", line 217, in get_template
    return self._load(srcfile, uri)
  File "/usr/lib/python2.7/dist-packages/mako/lookup.py", line 277, in _load
    **self.template_args)
  File "/usr/lib/python2.7/dist-packages/mako/template.py", line 205, in __init__
    module = self._compile_from_file(path, filename)
  File "/usr/lib/python2.7/dist-packages/mako/template.py", line 249, in _compile_from_file
    filename)
  File "/usr/lib/python2.7/dist-packages/mako/template.py", line 470, in _compile_text
    exec code in module.__dict__, module.__dict__
  File "_templates_article_mako", line 16, in <module>
ImportError: No module named templatedefs

有趣的是,我可以直接从模板中打印 sys.path

<%!
import sys
print sys.path
from website._webtools.templatedefs import strip_tags
%>

我可以确认,website 确实在路径中。而且,在其他所有的部署场景中,导入都能正常工作。

导入 websitewebsite._webtools 也没有问题。只有 website._webtools.templatedefs 这一部分出错了。

有没有人知道我可以在哪里查找可能出错的线索?

测试代码非常简单:

class BuildTestCase(unittest.TestCase):

    def setUp(self):
        self.tmpdir = tempfile.mkdtemp()
        self.cwd = os.getcwd()
        os.chdir(self.tmpdir)
        bootstrap(self.tmpdir, { # this initiates a new project
          "URL": "localhost",
          "TITLE": "Example",
          "DEFAULTS": {
              "AUTHOR": "John Doe",
          }
        })

    def test_initial_build(self):
        """Check, if building directly after bootstrap works"""
        build()

    def tearDown(self):
        os.chdir(self.cwd)
        shutil.rmtree(self.tmpdir)

补充:再提供一个诊断信息:我让 Mako 编译模板,并独立执行生成的 Python 文件,结果一切正常。我还把 templatedefs.py 简化到最基本的(只返回空字符串的定义),这样我就可以排除这个文件中的导入错误(或其他奇怪的问题)。

系统信息:Ubuntu 11.04,Python 2.7,Mako 0.3.6。

1 个回答

3

这确实让人抓狂。不过这里有一些事情要注意:

  1. ./nosetests:这个命令可以正常工作,并且所有9个测试都通过了。

  2. 'templatedefs'是'_webtools.__dict__'中唯一缺失的键,当你在你的mako模板中添加'from website import _webtools'并将'nosetests''python -m unittest tests.testWebsite'进行比较时,其他部分早已被导入。

  3. sys.path'python -m unittest tests.testWebsite'的情况下包含''(一个相对路径),但在'nosetests'的情况下,sys.path只包含绝对路径。这导致'website._webtools.__file__'的值不同:一个是相对路径['website/_webtools'],另一个是绝对路径['/home/username/tmp/website/_webtools']。因为你使用了os.chdir,所以相对路径就不再有效了。

所以:如果你想使用纯粹的unittest,可以在你的测试文件开头添加'import website._webtools.templatedefs'。这样可以确保在运行os.chdir时已经导入了templatedefs。我建议使用nose。希望这能帮到你。

撰写回答