Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is PyCompilerFlags in Python C API?

Tags:

python

cpython

If you checked Python C- API documentation about running python code via the C calls, you will always find mention to PyCompilerFlags, but nothing really describes what is it except the last portion of documentation and says nothing about its possible values and their effect on execution.

like image 911
Shady Atef Avatar asked Jul 18 '18 19:07

Shady Atef


1 Answers

PyCompilerFlags is the C API equivalent to the flags argument passed to compile and related functions in Python. This probably isn't at all obvious if you don't already know the Python docs forward and backward before looking at the CPython C-API docs.

From compile:

The optional arguments flags and dont_inherit control which future statements affect the compilation of source. If neither is present (or both are zero) the code is compiled with those future statements that are in effect in the code that is calling compile(). If the flags argument is given and dont_inherit is not (or is zero) then the future statements specified by the flags argument are used in addition to those that would be used anyway. If dont_inherit is a non-zero integer then the flags argument is it – the future statements in effect around the call to compile are ignored.

Future statements are specified by bits which can be bitwise ORed together to specify multiple statements. The bitfield required to specify a given feature can be found as the compiler_flag attribute on the _Feature instance in the __future__ module.

Following the link to future statements gives more details on how they work, and the link to the __future__ has a chart showing the list of future statements available.


Another thing that may not be obvious: each future feature flag corresponds to a flag that ends up in the co_flags attribute of a code object. So:

code = compile('1 <> 2', '', 'eval', flags=__future__.barry_as_FLUFL.compiler_flag)
assert code.co_flags & CO_FUTURE_BARRY_AS_BDFL

In C, if you pass struct PyCompilerFlags flags = { CO_FUTURE_BARRY_AS_BDFL } to get the same effect.

If you want to see the actual numeric values for those flags, you have to look up the corresponding CO_* constants in the C source or in the __future__ source.


Things are slightly different in the C API, in a few ways.

  • Rather than passing both flags and dont_inherit, you only pass flags, which is a complete set of all of the future statements you want to be in effect during the PyRun_* or PyCompile_* call.
  • Most of the functions take a PyCompile_Flags struct holding an int, instead of a raw int. This is just for the purpose of type checking; in memory, a struct holding an int is stored the same way as an int.
  • Many functions take their flags by pointer, so you can retrieve the possibly-updated set of flags after running the code.

Let's look at a complete example. I'll use Python 2.7 even though I've been linking to 3.7 docs, just because an example using print is simpler than one using forward annotations.

This code prints an empty tuple:

print()

But if you run the first one with PyRun_SimpleStringFlags, passing CO_FUTURE_PRINT_FUNCTION (0x10000) as the flags`, it will print a blank line, a la Python 3.

If you run this code:

from __future__ import print_function
print()

… then whether you passed in 0 or CO_FUTURE_PRINT_FUNCTION, it will print a blank line. And after the call, if you look at the flags you passed in by reference, it will have that CO_FUTURE_PRINT_FUNCTION or'd onto it. So, if you're compiling and running a chunk at a time, you can pass that same value along to the next string, and it'll inherit that future flag. (Much like when you write a future statement in the interactive interpreter, it affects all the statements you interpret after that.)

like image 142
abarnert Avatar answered Nov 13 '22 09:11

abarnert