Python 如何反省激活了哪些语言功能?

How can Python introspect on which language features are activated?

在这个 Python 2.7 代码中,应该用什么代替 GET_MY_COMPILER_FLAGS()

from __future__ import print_function

def ReportCompilerFlags():
    myFlags = GET_MY_COMPILER_FLAGS()  # how?
    import __future__
    for featureName in __future__.all_feature_names:
        thisFlag = getattr(__future__, featureName).compiler_flag
        print('File {fileName} has {featureName} turned {status}.'.format(
            fileName = __file__,
            featureName = featureName,
            status = 'ON' if (myFlags & thisFlag) else 'off',
        ))
ReportCompilerFlags()

期望的输出:

File /path/to/this/script.py has nested_scopes turned off.
File /path/to/this/script.py has generators turned off.
File /path/to/this/script.py has division turned off.
File /path/to/this/script.py has absolute_import turned off.
File /path/to/this/script.py has with_statement turned off.
File /path/to/this/script.py has print_function turned ON.
File /path/to/this/script.py has unicode_literals turned off.   

我知道我可以检查 globals() 以查看它是否包含适当命名的符号,而不是查看标志,但这样显然有可能出现误报和漏报。

更新: 我更新了标题,将这个问题从经典的 XY 问题中挖掘出来。要查询语言功能的激活状态,事实证明我应该查看的不仅仅是编译器标志,因为编译器实际上并没有将标志用于已经强制的功能。

您可以检查代码对象的co_flags。这包括所有 __future__ 标志和一堆用于其他事情的标志,比如代码对象是否用于生成器。

import inspect

all_flags = inspect.currentframe().f_code.co_flags

请注意,这告诉您哪些标志处于活动状态,而不一定是哪些功能处于活动状态。如果您的 Python 版本默认启用某个功能,则可能不会设置其标志。您可以检查要素对象的 getMandatoryRelease 以确定默认情况下标志是否打开:

import sys

def on_by_default(feature):
    return feature.getMandatoryRelease() <= sys.version_info

此外,请确保您拥有正确的代码对象。例如,如果你将它包装在一个库函数中,你应该确保你没有查看库函数自己的标志:

import inspect
import types

def code_flags(target=None):
    """
    Returns code flags for the caller of this function, or for a
    specified code or frame object.
    """
    if target is None:
        target = inspect.currentframe().f_back
    if isinstance(target, types.FrameType):
        target = target.f_code
    return target.co_flags

根据 user2357112 的回答和其他人的有用评论,这是我最终编写的库函数:

import sys
import inspect
import __future__

def features(stackBack=0):
    featureStatus = set()
    frame = None
    for featureName in __future__.all_feature_names:
        feature = getattr( __future__, featureName)
        if feature.getMandatoryRelease() <= sys.version_info:
            activated = True
        else:
            if frame is None:
                frame = inspect.stack()[abs(stackBack) + 1][0]
            activated = (frame.f_code.co_flags & feature.compiler_flag) != 0
        if activated:
            featureStatus.add(featureName)
    return featureStatus

假设我将其保存为 language.py。 Python 2.7.12 和 3.6.8 的各自输出是:

$ python -c 'import language; print(language.features())'
set(['with_statement', 'generators', 'nested_scopes'])

$ python3 -c 'import language; print(language.features())'
{'unicode_literals', 'with_statement', 'print_function', 'absolute_import', 'generators', 'division', 'nested_scopes'}

一个典型的用例可能是测试 'unicode_literals' in features() 检查您当前是否激活了该特定功能,或 'unicode_literals' in features(stackBack=1) 检查它是否在调用您的模块中激活。