2017-05-06 70 views
0

在爲Python 2.7和Python 3.x,we typically use # doctest: +IGNORE_EXCEPTION_DETAIL to paper over differences between Python 2.x and 3.x tracebacks都能工作的代碼編寫doctests和示例時。這需要照顧的BarError VS music.BarError,例如,但它分崩離析爲OSError(errno.ENOENT, ...)IOError(errno.ENOENT, ...),因爲,見:python 2.7,3.x doctest兼容性vs FileNotFoundError

import errno 

def testme(): 
    """ 
    Test doctest vs ENOENT 

    >>> testme() 
    Traceback (most recent call last): 
     ... 
    OSError: [Errno 2] so far so good 
    """ 
    raise OSError(errno.ENOENT, 'so far so good') 

if __name__ == '__main__': 
    import doctest 
    doctest.testmod() 

當這與python2.7運行,一切都很好:

$ python2 test3.py -v 
Trying: 
    testme() 
Expecting: 
    Traceback (most recent call last): 
     ... 
    OSError: [Errno 2] so far so good 
ok 
1 items had no tests: 
    __main__ 
1 items passed all tests: 
    1 tests in __main__.testme 
1 tests in 2 items. 
1 passed and 0 failed. 
Test passed. 

然而,當與Python 3.x運行,:

$ python3 test3.py 
********************************************************************** 
File "test3.py", line 7, in __main__.testme 
Failed example: 
    testme() 
Expected: 
    Traceback (most recent call last): 
     ... 
    OSError: [Errno 2] so far so good 
Got: 
    Traceback (most recent call last): 
     File "/usr/local/lib/python3.6/doctest.py", line 1330, in __run 
     compileflags, 1), test.globs) 
     File "<doctest __main__.testme[0]>", line 1, in <module> 
     testme() 
     File "test3.py", line 12, in testme 
     raise OSError(errno.ENOENT, 'so far so good') 
    FileNotFoundError: [Errno 2] so far so good 
********************************************************************** 
1 items had failures: 
    1 of 1 in __main__.testme 
***Test Failed*** 1 failures. 

的Python 3.6取代OSErrorFileNotFoundError

IOError發生同樣的事情。文件test4.py已將OSError替換爲raise中的IOError和文檔字符串。如果沒有引述的一切:

$ python2 test4.py 
$ python3 test4.py 
... 
    FileNotFoundError: [Errno 2] so far so good 
***Test Failed*** 1 failures. 

使用IGNORE_EXCEPTION_DETAIL沒有幫助,因爲(顯然),只有通過.跳過前綴了錯誤消息的第一個冒號之前,這裏的問題是字符串文字OSErrorIOError vs FileNotFoundError

我有一個基於裝飾者的解決方案,我將包括一個答案,但有沒有更好的方法來做到這一點?

回答

0

我有一個基於裝飾的解決方案,是不是有史以來最漂亮的事情:

import sys 

if sys.version_info[0] >= 3: 
    def enoent_doc(func): 
     "py3k compat around ENOENT" 
     func.__doc__ = func.__doc__.replace('OS:ENOENT', 
      'FileNotFoundError: [Errno 2]').replace('IO:ENOENT', 
      'FileNotFoundError: [Errno 2]') 
     return func 
else: 
    def enoent_doc(func): 
     "py2k compat around ENOENT" 
     func.__doc__ = func.__doc__.replace('OS:ENOENT', 
      'OSError: [Errno 2]').replace('IO:ENOENT', 
      'IOError: [Errno 2]') 
     return func 

@enoent_doc 
def testme(): 
    """ 
    Test doctest vs ENOENT 

    >>> testme() 
    Traceback (most recent call last): 
     ... 
    OS:ENOENT so far so good 
    """ 
    raise OSError(errno.ENOENT, 'so far so good') 

現在代碼可以在任一python2(2.7)或python3(3.X)運行。

裝飾者可能有點聰明(例如[Errno]價值不硬編碼2),但它的工作原理。

有沒有更好的解決辦法?