首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Python模拟ImportError

用Python模拟ImportError
EN

Stack Overflow用户
提问于 2010-03-20 01:21:51
回答 3查看 5.4K关注 0票数 12

我已经试了将近两个小时了,没有任何运气。

我有一个模块看起来是这样的:

代码语言:javascript
复制
try:
    from zope.component import queryUtility  # and things like this
except ImportError:
    # do some fallback operations <-- how to test this?

稍后在代码中:

代码语言:javascript
复制
try:
    queryUtility(foo)
except NameError:
    # do some fallback actions <-- this one is easy with mocking 
    # zope.component.queryUtility to raise a NameError

有什么想法吗?

编辑:

亚历克斯的建议似乎行不通:

代码语言:javascript
复制
>>> import __builtin__
>>> realimport = __builtin__.__import__
>>> def fakeimport(name, *args, **kw):
...     if name == 'zope.component':
...         raise ImportError
...     realimport(name, *args, **kw)
...
>>> __builtin__.__import__ = fakeimport

在运行测试时:

代码语言:javascript
复制
aatiis@aiur ~/work/ao.shorturl $ ./bin/test --coverage .
Running zope.testing.testrunner.layer.UnitTests tests:
  Set up zope.testing.testrunner.layer.UnitTests in 0.000 seconds.


Error in test /home/aatiis/work/ao.shorturl/src/ao/shorturl/shorturl.txt
Traceback (most recent call last):
  File "/usr/lib64/python2.5/unittest.py", line 260, in run
    testMethod()
  File "/usr/lib64/python2.5/doctest.py", line 2123, in runTest
    test, out=new.write, clear_globs=False)
  File "/usr/lib64/python2.5/doctest.py", line 1361, in run
    return self.__run(test, compileflags, out)
  File "/usr/lib64/python2.5/doctest.py", line 1282, in __run
    exc_info)
  File "/usr/lib64/python2.5/doctest.py", line 1148, in report_unexpected_exception
    'Exception raised:\n' + _indent(_exception_traceback(exc_info)))
  File "/usr/lib64/python2.5/doctest.py", line 1163, in _failure_header
    out.append(_indent(source))
  File "/usr/lib64/python2.5/doctest.py", line 224, in _indent
    return re.sub('(?m)^(?!$)', indent*' ', s)
  File "/usr/lib64/python2.5/re.py", line 150, in sub
    return _compile(pattern, 0).sub(repl, string, count)
  File "/usr/lib64/python2.5/re.py", line 239, in _compile
    p = sre_compile.compile(pattern, flags)
  File "/usr/lib64/python2.5/sre_compile.py", line 507, in compile
    p = sre_parse.parse(p, flags)
AttributeError: 'NoneType' object has no attribute 'parse'



Error in test BaseShortUrlHandler (ao.shorturl)
Traceback (most recent call last):
  File "/usr/lib64/python2.5/unittest.py", line 260, in run
    testMethod()
  File "/usr/lib64/python2.5/doctest.py", line 2123, in runTest
    test, out=new.write, clear_globs=False)
  File "/usr/lib64/python2.5/doctest.py", line 1351, in run
    self.debugger = _OutputRedirectingPdb(save_stdout)
  File "/usr/lib64/python2.5/doctest.py", line 324, in __init__
    pdb.Pdb.__init__(self, stdout=out)
  File "/usr/lib64/python2.5/pdb.py", line 57, in __init__
    cmd.Cmd.__init__(self, completekey, stdin, stdout)
  File "/usr/lib64/python2.5/cmd.py", line 90, in __init__
    import sys
  File "<doctest shorturl.txt[10]>", line 4, in fakeimport
NameError: global name 'realimport' is not defined

但是,当我从python交互控制台运行相同的代码时,会执行工作。

更多编辑:

我使用的是zope.testing和一个测试文件shorturl.txt,它包含所有特定于模块这一部分的测试。首先,我使用可用的zope.component导入模块,以演示和测试通常的用法。zope.*包的缺失被认为是一种边缘情况,所以我稍后将对其进行测试。因此,在使reload()不可用之后,我必须以某种方式对模块进行zope.*处理。

到目前为止,我甚至尝试使用tempfile.mktempdir()并在tempdir中空zope/__init__.pyzope/component/__init__.py文件,然后将tempdir插入sys.path[0],并从sys.modules中删除旧的zope.*包。

也没起作用。

更多编辑:

与此同时,我试过这个:

代码语言:javascript
复制
>>> class NoZope(object):
...     def find_module(self, fullname, path):
...         if fullname.startswith('zope'):
...             raise ImportError
... 

>>> import sys
>>> sys.path.insert(0, NoZope())

它在测试套件的命名空间(= shorturl.txt中的所有导入)中运行良好,但它没有在我的主模块ao.shorturl中执行。甚至当我reload()的时候也不会。知道为什么吗?

代码语言:javascript
复制
>>> import zope  # ok, this raises an ImportError
>>> reload(ao.shorturl)    <module ...>

导入zope.interfaces会引发ImportError,因此它不会到达我导入zope.component的部分,而则会保留在ao.shorturl命名空间中。为什么?!

代码语言:javascript
复制
>>> ao.shorturl.zope.component  # why?! 
<module ...>
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-03-20 01:50:03

只需将猴子放入builtins --您自己版本的__import__ --当它认识到它正在被调用到您想要模拟错误的特定模块时,它可以引发任何您想要的东西。有关详细信息,请参见医生们。大致如下:

代码语言:javascript
复制
try:
    import builtins
except ImportError:
    import __builtin__ as builtins
realimport = builtins.__import__

def myimport(name, globals, locals, fromlist, level):
    if ...:
        raise ImportError
    return realimport(name, globals, locals, fromlist, level)

builtins.__import__ = myimport

代替...,您可以硬编码name == 'zope.component',或者使用自己的回调来更灵活地安排事情,这可以使导入在不同情况下按需增加,这取决于您的特定测试需求,而无需编写多个__import__-alike函数;-)。

还请注意,如果您使用的是import zope.componentfrom zope.component import something,而不是from zope import component,那么name将是'zope',而'component'将是fromlist中的唯一项目。

编辑__import__函数的文档表示要导入的名称是builtin (类似于Python3中的名称),但实际上您需要__builtins__ --我已经编辑了上面的代码,这样它就可以工作了。

票数 10
EN

Stack Overflow用户

发布于 2010-03-20 19:27:48

这就是我在单元测试中所做的。

它使用PEP-302“新进口钩”。(警告: PEP-302文档和我所链接的更简洁的发布说明并不完全准确。)

我使用meta_path,因为它在导入序列中越早越好。

如果模块已经被导入(在我的例子中,因为前面的单元测试是针对它的),那么在对依赖模块执行reload之前,有必要从reload中删除它。

代码语言:javascript
复制
 # Ensure we fallback to using ~/.pif if XDG doesn't exist.

 >>> import sys

 >>> class _():
 ... def __init__(self, modules):
 ...  self.modules = modules
 ...
 ...  def find_module(self, fullname, path=None):
 ...  if fullname in self.modules:
 ...   raise ImportError('Debug import failure for %s' % fullname)

 >>> fail_loader = _(['xdg.BaseDirectory'])
 >>> sys.meta_path.append(fail_loader)

 >>> del sys.modules['xdg.BaseDirectory']

 >>> reload(pif.index) #doctest: +ELLIPSIS
 <module 'pif.index' from '...'>

 >>> pif.index.CONFIG_DIR == os.path.expanduser('~/.pif')
 True

 >>> sys.meta_path.remove(fail_loader)

pif.index中的代码如下所示:

代码语言:javascript
复制
try:
    import xdg.BaseDirectory

    CONFIG_DIR = os.path.join(xdg.BaseDirectory.xdg_data_home, 'pif')
except ImportError:
    CONFIG_DIR = os.path.expanduser('~/.pif')

要回答为什么新重新加载的模块具有旧的和新的加载的属性的问题,以下是两个示例文件。

第一个是带有导入失败案例的模块y

代码语言:javascript
复制
# y.py

try:
    import sys

    _loaded_with = 'sys'
except ImportError:
    import os

    _loaded_with = 'os'

第二个是x,它演示了在重新加载模块时,离开句柄会如何影响其属性。

代码语言:javascript
复制
# x.py

import sys

import y

assert y._loaded_with == 'sys'
assert y.sys

class _():
    def __init__(self, modules):
        self.modules = modules
        
    def find_module(self, fullname, path=None):
        if fullname in self.modules:
            raise ImportError('Debug import failure for %s' % fullname)

# Importing sys will not raise an ImportError.
fail_loader = _(['sys'])
sys.meta_path.append(fail_loader)

# Demonstrate that reloading doesn't work if the module is already in the
# cache.

reload(y)

assert y._loaded_with == 'sys'
assert y.sys

# Now we remove sys from the modules cache, and try again.
del sys.modules['sys']

reload(y)

assert y._loaded_with == 'os'
assert y.sys
assert y.os

# Now we remove the handles to the old y so it can get garbage-collected.
del sys.modules['y']
del y

import y

assert y._loaded_with == 'os'
try:
    assert y.sys
except AttributeError:
    pass
assert y.os
票数 3
EN

Stack Overflow用户

发布于 2015-07-02 18:01:33

如果您不介意更改程序本身,也可以将导入调用放入函数中,并在测试中对其进行修补。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2481511

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档