1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/python/lib/core/py_util.h"
17
18 // Place `<locale>` before <Python.h> to avoid build failure in macOS.
19 #include <locale>
20
21 // The empty line above is on purpose as otherwise clang-format will
22 // automatically move <Python.h> before <locale>.
23 #include <Python.h>
24
25 #include "tensorflow/core/lib/core/errors.h"
26 #include "tensorflow/core/lib/strings/strcat.h"
27
28 namespace tensorflow {
29 namespace {
30
31 // py.__class__.__name__
ClassName(PyObject * py)32 const char* ClassName(PyObject* py) {
33 /* PyPy doesn't have a separate C API for old-style classes. */
34 #if PY_MAJOR_VERSION < 3 && !defined(PYPY_VERSION)
35 if (PyClass_Check(py))
36 return PyString_AS_STRING(
37 CHECK_NOTNULL(reinterpret_cast<PyClassObject*>(py)->cl_name));
38 if (PyInstance_Check(py))
39 return PyString_AS_STRING(CHECK_NOTNULL(
40 reinterpret_cast<PyInstanceObject*>(py)->in_class->cl_name));
41 #endif
42 if (Py_TYPE(py) == &PyType_Type) {
43 return reinterpret_cast<PyTypeObject*>(py)->tp_name;
44 }
45 return Py_TYPE(py)->tp_name;
46 }
47
48 } // end namespace
49
50 // Returns a PyObject containing a string, or null
TryAppendTraceback(PyObject * ptype,PyObject * pvalue,PyObject * ptraceback,string * out)51 void TryAppendTraceback(PyObject* ptype, PyObject* pvalue, PyObject* ptraceback,
52 string* out) {
53 // The "traceback" module is assumed to be imported already by script_ops.py.
54 PyObject* tb_module = PyImport_AddModule("traceback");
55
56 if (!tb_module) {
57 return;
58 }
59
60 PyObject* format_exception =
61 PyObject_GetAttrString(tb_module, "format_exception");
62
63 if (!format_exception) {
64 return;
65 }
66
67 if (!PyCallable_Check(format_exception)) {
68 Py_DECREF(format_exception);
69 return;
70 }
71
72 PyObject* ret_val = PyObject_CallFunctionObjArgs(format_exception, ptype,
73 pvalue, ptraceback, nullptr);
74 Py_DECREF(format_exception);
75
76 if (!ret_val) {
77 return;
78 }
79
80 if (!PyList_Check(ret_val)) {
81 Py_DECREF(ret_val);
82 return;
83 }
84
85 Py_ssize_t n = PyList_GET_SIZE(ret_val);
86 for (Py_ssize_t i = 0; i < n; ++i) {
87 PyObject* v = PyList_GET_ITEM(ret_val, i);
88 #if PY_MAJOR_VERSION < 3
89 strings::StrAppend(out, PyString_AS_STRING(v), "\n");
90 #else
91 strings::StrAppend(out, PyUnicode_AsUTF8(v), "\n");
92 #endif
93 }
94
95 // Iterate through ret_val.
96 Py_DECREF(ret_val);
97 }
98
PyExceptionFetch()99 string PyExceptionFetch() {
100 CHECK(PyErr_Occurred())
101 << "Must only call PyExceptionFetch after an exception.";
102 PyObject* ptype;
103 PyObject* pvalue;
104 PyObject* ptraceback;
105 PyErr_Fetch(&ptype, &pvalue, &ptraceback);
106 PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
107 string err = ClassName(ptype);
108 if (pvalue) {
109 PyObject* str = PyObject_Str(pvalue);
110
111 if (str) {
112 #if PY_MAJOR_VERSION < 3
113 strings::StrAppend(&err, ": ", PyString_AS_STRING(str), "\n");
114 #else
115 strings::StrAppend(&err, ": ", PyUnicode_AsUTF8(str), "\n");
116 #endif
117 Py_DECREF(str);
118 } else {
119 strings::StrAppend(&err, "(unknown error message)\n");
120 }
121
122 TryAppendTraceback(ptype, pvalue, ptraceback, &err);
123
124 Py_DECREF(pvalue);
125 }
126 Py_DECREF(ptype);
127 Py_XDECREF(ptraceback);
128 return err;
129 }
130
131 } // end namespace tensorflow
132