File: PythonBase.cpp

package info (click to toggle)
freeorion 0.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 194,940 kB
  • sloc: cpp: 186,508; python: 40,969; ansic: 1,164; xml: 719; makefile: 32; sh: 7
file content (160 lines) | stat: -rw-r--r-- 5,228 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "PythonBase.h"

#include "../util/Directories.h"
#include "../util/Logger.h"
#include "CommonWrappers.h"

#include <boost/filesystem.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python/list.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/docstring_options.hpp>

namespace fs = boost::filesystem;
namespace py = boost::python;

// Python module for logging functions
BOOST_PYTHON_MODULE(freeorion_logger) {
    boost::python::docstring_options doc_options(true, true, false);
    FreeOrionPython::WrapLogger();
}

PythonBase::~PythonBase()
{ Finalize(); }

bool PythonBase::Initialize() {
    if (!PythonCommon::Initialize()) {

#if defined(MS_WINDOWS)
        // forces stream encoding to UTF8, which will hopefully fix issues on windows with non-english locale settings
        const std::string ENCODING{"UTF-8"};    // specifying "C.UTF-8" causes Py_Initialize() call to fail for me -Geoff
        auto encoding_result = Py_SetStandardStreamEncoding(ENCODING.c_str(), ENCODING.c_str());
        DebugLogger() << "Python standard stream encoding set to: " << ENCODING << " with result: " << encoding_result;
#endif

        return false;
    }

    DebugLogger() << "Initializing C++ interfaces for Python";

    try {
        // get main namespace, needed to run other interpreted code
        py::object py_main = py::import("__main__");
        py::dict py_namespace = py::extract<py::dict>(py_main.attr("__dict__"));
        m_namespace = py_namespace;

        // add the directory containing common Python modules used by all Python scripts to Python sys.path
        AddToSysPath(GetPythonCommonDir());
    } catch (const py::error_already_set&) {
        HandleErrorAlreadySet();
        ErrorLogger() << "Unable to initialize FreeOrion Python namespace and set path";
        return false;
    } catch (...) {
        ErrorLogger() << "Unable to initialize FreeOrion Python namespace and set path";
        return false;
    }

    try {
        // Allow C++ modules implemented by derived classes to be imported
        // within Python code
        if (!InitModules()) {
            ErrorLogger() << "Unable to initialize FreeOrion Python modules";
            return false;
        }
    } catch (const py::error_already_set&) {
        HandleErrorAlreadySet();
        ErrorLogger() << "Unable to initialize FreeOrion Python modules (exception caught)";
        return false;
    } catch (...) {
        ErrorLogger() << "Unable to initialize FreeOrion Python modules (exception caught)";
        return false;
    }

    DebugLogger() << "FreeOrion Python interface successfully initialized!";
    return true;
}

bool PythonBase::InitCommonImports() {
    // allow the "freeorion_logger" C++ module to be imported within Python code
    if (PyImport_AppendInittab("freeorion_logger", &PyInit_freeorion_logger) == -1) {
        ErrorLogger() << "Unable to initialize freeorion_logger import";
        return false;
    }
    if (!InitImports()) {
        ErrorLogger() << "Unable to initialize imports";
        return false;
    }
    return true;
}

void PythonBase::Finalize() {
    if (Py_IsInitialized()) {
        // cleanup python objects before interpterer shutdown
        m_namespace = boost::none;
        if (m_python_module_error != nullptr) {
            (*m_python_module_error) = py::object();
            m_python_module_error = nullptr;
        }
        PythonCommon::Finalize();
        DebugLogger() << "Cleaned up FreeOrion Python interface";
    }
}

void PythonBase::SetCurrentDir(const fs::path& dir) {
    if (!fs::exists(dir)) {
        ErrorLogger() << "Tried setting current dir to non-existing dir: " << PathToString(dir);
        return;
    }
    py::str script = "import os\n"
        "os.chdir(r'";
    script += dir.native();
    script += "')\n"
        "print ('Python current directory set to', os.getcwd())";
    exec(script, *m_namespace, *m_namespace);
}

void PythonBase::AddToSysPath(const fs::path& dir) {
    if (!fs::exists(dir)) {
        ErrorLogger() << "Tried adding non-existing dir to sys.path: " << PathToString(dir);
        return;
    }
    py::str script = "import sys\n"
        "sys.path.append(r'";
    script += dir.native();
    script += "')";
    exec(script, *m_namespace, *m_namespace);
}

void PythonBase::SetErrorModule(py::object& module)
{ m_python_module_error = &module; }

std::vector<std::string> PythonBase::ErrorReport() {
    std::vector<std::string> err_list;

    if (m_python_module_error) {
        py::object f = m_python_module_error->attr("error_report");
        if (!f) {
            ErrorLogger() << "Unable to call Python function error_report ";
            return err_list;
        }

        py::list py_err_list;
        try { py_err_list = py::extract<py::list>(f()); }
        catch (const py::error_already_set&) {
            HandleErrorAlreadySet();
            return err_list;
        }

        for (int i = 0; i < len(py_err_list); i++) {
            err_list.push_back(py::extract<std::string>(py_err_list[i]));
        }
    }

    return err_list;
}

fs::path GetPythonDir()
{ return GetResourceDir() / "python"; }

fs::path GetPythonCommonDir()
{ return GetPythonDir(); }