Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can Python introspect on which language features are activated?

In this Python 2.7 code, what should take the place of 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()

Desired output:

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.   

I know I could inspect globals() to see if it contains the appropriately named symbols instead of looking at the flags, but false positives and false negatives are clearly both possible that way.

Update: I updated the title to dig this question out of the classic XY problem. To query activation status of language features, it turns out I should be looking at more than just compiler flags, since the compiler does not actually use the flag for a feature that is already mandatory.

like image 971
jez Avatar asked Sep 07 '25 09:09

jez


2 Answers

You can inspect the code object's co_flags. This includes all __future__ flags and a bunch of flags used for other things, like whether the code object is for a generator.

import inspect

all_flags = inspect.currentframe().f_code.co_flags

Note that this tells you what flags are active, not necessarily what features are active. If a feature is on by default in your version of Python, its flag probably won't be set. You can check the feature object's getMandatoryRelease to determine whether a flag is on by default:

import sys

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

Also, make sure you have the right code object. For example, if you wrap this in a library function, you should make sure you're not looking at the library function's own flags:

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
like image 121
user2357112 supports Monica Avatar answered Sep 10 '25 08:09

user2357112 supports Monica


Based on user2357112's answer and others' helpful comments, here's the library function I ended up writing:

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

Let's say I save this as language.py. The respective outputs for Python 2.7.12 and 3.6.8 are:

$ 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'}

A typical use case might be the test 'unicode_literals' in features() to check whether you currently have that particular feature active, or 'unicode_literals' in features(stackBack=1) to check whether it's active in whichever module called you.

like image 30
jez Avatar answered Sep 10 '25 09:09

jez