不能让Mypy使用__init__.py别名

时间:2017-06-03 13:12:59

标签: python mypy

以下是我的问题的最小例子:

import * as WeatherActions from '../actions/weatherActions';

const initialState = {
    weatherDetails: {
        area: '',
        temperature: 'sdf'
    }
};

function WeatherReducer(state = initialState, action) {
    console.log('in WeatherReducer, this is action: ' + JSON.stringify(action));


    switch (action.type) {
        case WeatherActions.ActionTypes.WEATHER.LOAD_WEATHER:
            return [...state, Object.assign({}, action.temperature.data)];
        default:
            return state;
    }

}
export default WeatherReducer;

模块的Mypy输出或__init__.py:

[test/__init__.py]
from test.test1 import Test1
from test.test2 import Test2

[test/test1.py]
class Test1:
    pass

[test/test2.py]
from test import Test1

class Test2:
    pass

Code本身在Python 2和Python 3上都运行良好。

1 个答案:

答案 0 :(得分:2)

这是因为您的代码有一个导入周期 - 为了让mypy解析test/__init__.py,它需要准确理解Test2是什么。 (毕竟,如果您决定稍后在该文件中使用Test2 /调用其中一个方法会怎样?那么mypy需要知道输出是什么)。

因此,它会导致导入,有效地暂停,并跳转到试图理解test/test2.py正在做什么*。

但在test/test2.py内,我们遇到了完全相同的问题 - 我们看到了导入,需要跳回test/__init__.py才能了解Test1是什么......但我们还没有完成解析该文件!

这是mypy与Python运行时不同的地方,fyi - mypy一次只能解析整个文件,但Python运行时实际暂停执行以运行test/test2.py。这意味着当您执行from test import Test1时,test模块会有一个部分完整的符号空间,恰好当前只包含Test1,而不是Test1Test2 ,这就是你的代码在运行时工作的原因。

在这种情况下,修复方法是将test/test2.py中的导入修改为:

from test.test1 import Test1

这会打破导入周期。

*这实际上并不是mypy的作用 - 它实际上做的是尝试通过首先识别所有strongly connected components(SCC)来解决导入周期 - 每个SCC基本上都是一个导入周期。

然后应用一些启发式方法来确定SCC中文件的处理顺序,但这是一个不完美的过程,无法解决所有导入周期。

例如,在您的情况下,无论我们是先处理test还是test.test2,我们都会遇到问题。

您可以通过使用-v标志重新运行mypy来查看SCC mypy标识(对于详细模式)。

您可以在源代码here中找到有关mypy使用的算法的更多详细信息。有关导入周期解析算法的具体细节可以稍微降低here

(我怀疑整个评论有点过时/不完整,fyi - 还有其他一些与mypy的静默导入机制相关的皱纹及其增量模式机制并没有真正解释。)

可以找到mypy用于订购SCC的确切启发式信息here。 (基本上,我们用于导入内容的确切语法可以提供有关如何订购SCC的一些提示。)