2

I've got an extension module that needs to support Python 2 and Python 3. When it loads, I want to do something like from __future__ import print_function so that if anyone tries to do this:

PyRun_SimpleString("print 'foo'");

it will fail, but if they do this:

PyRun_SimpleString("print('foo', file=sys.stdout)");

it will succeed. I've tried a number of things, including PyRun_SimpleString of the from future statement. like this:

PyRun_SimpleString("from __future__ import print_function");

and I've also tried calling PyImport_ImportModuleEx like this:

PyImport_ImportModuleEx("__future__", nullptr, nullptr, fromlist);

where fromlist is a PyObject representing the list ["print_function"]

Neither of these works. Is it possible to make this work?

Zachary Turner
  • 738
  • 4
  • 24
  • 1
    Why is your extension module using `PyRun_SimpleString`, especially to print things? – user2357112 Oct 23 '15 at 22:09
  • It's not exactly, this is for the sake of illustrating the problem. It does, however, expose an embedded interpreter. i.e. you can perform some actions within the program to drop to a python prompt, or you can run some commands that execute python code and display the interpreter's output (those commands are passed through via `PyRun_SimpleString`. What I need is a way to disable print statements so that if anyone runs a print statement, they will get an error. – Zachary Turner Oct 23 '15 at 22:15
  • That's a pretty different problem from the one you're asking about, and the solution is pretty different. – user2357112 Oct 23 '15 at 22:33
  • Umm, ok? I'm not sure I follow. The commands users are entering get passed through to `PyRun_SimpleString`. Therefore by making sure that `PyRun_SimpleString("print 'foo'")` generates an error, I also make sure that it generates an error when they enter that command. Which is the problem I am trying to solve. It might help me to understand what you're getting at if you were a little bit less ambiguous in your statements. – Zachary Turner Oct 23 '15 at 22:35
  • The question you've asked is about getting the effects of future statements in calls to `PyRun_SimpleString`, while the problem you're actually trying to solve is running user-provided Python code under the effects of future statements. `PyRun_SimpleString` isn't the tool for the job. – user2357112 Oct 23 '15 at 22:37

1 Answers1

3

PyRun_SimpleString doesn't have anywhere to specify compiler flags, but there's a whole host of related functions with more options available. For example, PyRun_SimpleStringFlags does what PyRun_SimpleString does, but with a flags argument to specify compiler flags. __future__ statements executed with PyRun_SimpleStringFlags can modify the flags, and the change will be seen by other calls using the same flags.

You can initialize flags with future statements, or with the C-level flag definitions. Most of the possible flags aren't listed in the C-api documentation (since the C-api documentation kind of sucks), but you can find them in the __future__ module or in several parts of the Python source code.

>>> import __future__
>>> for name in dir(__future__):
...     if name.startswith('CO_'):
...         print name
...
CO_FUTURE_ABSOLUTE_IMPORT
CO_FUTURE_DIVISION
CO_FUTURE_PRINT_FUNCTION
CO_FUTURE_UNICODE_LITERALS
CO_FUTURE_WITH_STATEMENT
CO_GENERATOR_ALLOWED
CO_NESTED

For example, you could run a string under the effects of from __future__ import print_function with

PyCompilerFlags flags = {CO_FUTURE_PRINT_FUNCTION};
PyRun_SimpleStringFlags(command, &flags);

If you want to save the effects of user-executed future statements, you'd save the PyCompilerFlags struct somewhere and use the same one for all calls.


With that said, I don't think automatically applying from __future__ import print_function is the right thing to do. It'll surprise people expecting the default behavior of whatever interpreter they're on. Also, PyRun_InteractiveOneFlags might be useful for letting the user input multi-line Python statements.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Thanks, that's helpful. Is there any way to get the set of flags the interpreter is currently under the effects of? This way I could get that set of flags, and add in `CO_FUTURE_PRINT_FUNCTION`. This is in an extension module that has to support both Py2 and Py3 with a single codebase and -- in particular -- a single set of tests. So mandating print function is the best way (that I know of) to ensure that the test suite always passes all the time. – Zachary Turner Oct 23 '15 at 23:03
  • @ZacharyTurner: I don't think that's something the interpreter exposes. I did figure out how to preserve flags from one statement to the next, though. As for the test suite, if you preserve user-executed future statements, you can just put `from __future__ import print_function` in your test setup. – user2357112 Oct 23 '15 at 23:24