111986Sandreas.sandberg@arm.com/*
211986Sandreas.sandberg@arm.com    pybind11/exec.h: Support for evaluating Python expressions and statements
311986Sandreas.sandberg@arm.com    from strings and files
411986Sandreas.sandberg@arm.com
511986Sandreas.sandberg@arm.com    Copyright (c) 2016 Klemens Morgenstern <klemens.morgenstern@ed-chemnitz.de> and
611986Sandreas.sandberg@arm.com                       Wenzel Jakob <wenzel.jakob@epfl.ch>
711986Sandreas.sandberg@arm.com
811986Sandreas.sandberg@arm.com    All rights reserved. Use of this source code is governed by a
911986Sandreas.sandberg@arm.com    BSD-style license that can be found in the LICENSE file.
1011986Sandreas.sandberg@arm.com*/
1111986Sandreas.sandberg@arm.com
1211986Sandreas.sandberg@arm.com#pragma once
1311986Sandreas.sandberg@arm.com
1411986Sandreas.sandberg@arm.com#include "pybind11.h"
1511986Sandreas.sandberg@arm.com
1612391Sjason@lowepower.comNAMESPACE_BEGIN(PYBIND11_NAMESPACE)
1711986Sandreas.sandberg@arm.com
1811986Sandreas.sandberg@arm.comenum eval_mode {
1911986Sandreas.sandberg@arm.com    /// Evaluate a string containing an isolated expression
2011986Sandreas.sandberg@arm.com    eval_expr,
2111986Sandreas.sandberg@arm.com
2211986Sandreas.sandberg@arm.com    /// Evaluate a string containing a single statement. Returns \c none
2311986Sandreas.sandberg@arm.com    eval_single_statement,
2411986Sandreas.sandberg@arm.com
2511986Sandreas.sandberg@arm.com    /// Evaluate a string containing a sequence of statement. Returns \c none
2611986Sandreas.sandberg@arm.com    eval_statements
2711986Sandreas.sandberg@arm.com};
2811986Sandreas.sandberg@arm.com
2911986Sandreas.sandberg@arm.comtemplate <eval_mode mode = eval_expr>
3012391Sjason@lowepower.comobject eval(str expr, object global = globals(), object local = object()) {
3111986Sandreas.sandberg@arm.com    if (!local)
3211986Sandreas.sandberg@arm.com        local = global;
3311986Sandreas.sandberg@arm.com
3411986Sandreas.sandberg@arm.com    /* PyRun_String does not accept a PyObject / encoding specifier,
3511986Sandreas.sandberg@arm.com       this seems to be the only alternative */
3611986Sandreas.sandberg@arm.com    std::string buffer = "# -*- coding: utf-8 -*-\n" + (std::string) expr;
3711986Sandreas.sandberg@arm.com
3811986Sandreas.sandberg@arm.com    int start;
3911986Sandreas.sandberg@arm.com    switch (mode) {
4011986Sandreas.sandberg@arm.com        case eval_expr:             start = Py_eval_input;   break;
4111986Sandreas.sandberg@arm.com        case eval_single_statement: start = Py_single_input; break;
4211986Sandreas.sandberg@arm.com        case eval_statements:       start = Py_file_input;   break;
4311986Sandreas.sandberg@arm.com        default: pybind11_fail("invalid evaluation mode");
4411986Sandreas.sandberg@arm.com    }
4511986Sandreas.sandberg@arm.com
4611986Sandreas.sandberg@arm.com    PyObject *result = PyRun_String(buffer.c_str(), start, global.ptr(), local.ptr());
4711986Sandreas.sandberg@arm.com    if (!result)
4811986Sandreas.sandberg@arm.com        throw error_already_set();
4911986Sandreas.sandberg@arm.com    return reinterpret_steal<object>(result);
5011986Sandreas.sandberg@arm.com}
5111986Sandreas.sandberg@arm.com
5212391Sjason@lowepower.comtemplate <eval_mode mode = eval_expr, size_t N>
5312391Sjason@lowepower.comobject eval(const char (&s)[N], object global = globals(), object local = object()) {
5412391Sjason@lowepower.com    /* Support raw string literals by removing common leading whitespace */
5512391Sjason@lowepower.com    auto expr = (s[0] == '\n') ? str(module::import("textwrap").attr("dedent")(s))
5612391Sjason@lowepower.com                               : str(s);
5712391Sjason@lowepower.com    return eval<mode>(expr, global, local);
5812391Sjason@lowepower.com}
5912391Sjason@lowepower.com
6012391Sjason@lowepower.cominline void exec(str expr, object global = globals(), object local = object()) {
6112391Sjason@lowepower.com    eval<eval_statements>(expr, global, local);
6212391Sjason@lowepower.com}
6312391Sjason@lowepower.com
6412391Sjason@lowepower.comtemplate <size_t N>
6512391Sjason@lowepower.comvoid exec(const char (&s)[N], object global = globals(), object local = object()) {
6612391Sjason@lowepower.com    eval<eval_statements>(s, global, local);
6712391Sjason@lowepower.com}
6812391Sjason@lowepower.com
6911986Sandreas.sandberg@arm.comtemplate <eval_mode mode = eval_statements>
7012391Sjason@lowepower.comobject eval_file(str fname, object global = globals(), object local = object()) {
7111986Sandreas.sandberg@arm.com    if (!local)
7211986Sandreas.sandberg@arm.com        local = global;
7311986Sandreas.sandberg@arm.com
7411986Sandreas.sandberg@arm.com    int start;
7511986Sandreas.sandberg@arm.com    switch (mode) {
7611986Sandreas.sandberg@arm.com        case eval_expr:             start = Py_eval_input;   break;
7711986Sandreas.sandberg@arm.com        case eval_single_statement: start = Py_single_input; break;
7811986Sandreas.sandberg@arm.com        case eval_statements:       start = Py_file_input;   break;
7911986Sandreas.sandberg@arm.com        default: pybind11_fail("invalid evaluation mode");
8011986Sandreas.sandberg@arm.com    }
8111986Sandreas.sandberg@arm.com
8211986Sandreas.sandberg@arm.com    int closeFile = 1;
8311986Sandreas.sandberg@arm.com    std::string fname_str = (std::string) fname;
8411986Sandreas.sandberg@arm.com#if PY_VERSION_HEX >= 0x03040000
8511986Sandreas.sandberg@arm.com    FILE *f = _Py_fopen_obj(fname.ptr(), "r");
8611986Sandreas.sandberg@arm.com#elif PY_VERSION_HEX >= 0x03000000
8711986Sandreas.sandberg@arm.com    FILE *f = _Py_fopen(fname.ptr(), "r");
8811986Sandreas.sandberg@arm.com#else
8911986Sandreas.sandberg@arm.com    /* No unicode support in open() :( */
9011986Sandreas.sandberg@arm.com    auto fobj = reinterpret_steal<object>(PyFile_FromString(
9111986Sandreas.sandberg@arm.com        const_cast<char *>(fname_str.c_str()),
9211986Sandreas.sandberg@arm.com        const_cast<char*>("r")));
9311986Sandreas.sandberg@arm.com    FILE *f = nullptr;
9411986Sandreas.sandberg@arm.com    if (fobj)
9511986Sandreas.sandberg@arm.com        f = PyFile_AsFile(fobj.ptr());
9611986Sandreas.sandberg@arm.com    closeFile = 0;
9711986Sandreas.sandberg@arm.com#endif
9811986Sandreas.sandberg@arm.com    if (!f) {
9911986Sandreas.sandberg@arm.com        PyErr_Clear();
10011986Sandreas.sandberg@arm.com        pybind11_fail("File \"" + fname_str + "\" could not be opened!");
10111986Sandreas.sandberg@arm.com    }
10211986Sandreas.sandberg@arm.com
10312037Sandreas.sandberg@arm.com#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
10412037Sandreas.sandberg@arm.com    PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
10512037Sandreas.sandberg@arm.com                                  local.ptr());
10612037Sandreas.sandberg@arm.com    (void) closeFile;
10712037Sandreas.sandberg@arm.com#else
10811986Sandreas.sandberg@arm.com    PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
10911986Sandreas.sandberg@arm.com                                    local.ptr(), closeFile);
11012037Sandreas.sandberg@arm.com#endif
11112037Sandreas.sandberg@arm.com
11211986Sandreas.sandberg@arm.com    if (!result)
11311986Sandreas.sandberg@arm.com        throw error_already_set();
11411986Sandreas.sandberg@arm.com    return reinterpret_steal<object>(result);
11511986Sandreas.sandberg@arm.com}
11611986Sandreas.sandberg@arm.com
11712391Sjason@lowepower.comNAMESPACE_END(PYBIND11_NAMESPACE)
118