1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 /** \file
18  * \ingroup pythonintern
19  *
20  * This file contains Blender/Python utility functions to help implementing API's.
21  * This is not related to a particular module.
22  */
23 
24 #include <Python.h>
25 
26 #include "BLI_dynstr.h"
27 #include "BLI_listbase.h"
28 #include "BLI_utildefines.h"
29 
30 #include "bpy_capi_utils.h"
31 
32 #include "MEM_guardedalloc.h"
33 
34 #include "BKE_context.h"
35 #include "BKE_report.h"
36 
37 #include "BLT_translation.h"
38 
39 #include "../generic/py_capi_utils.h"
40 
BPy_enum_as_string(const EnumPropertyItem * item)41 char *BPy_enum_as_string(const EnumPropertyItem *item)
42 {
43   DynStr *dynstr = BLI_dynstr_new();
44 
45   /* We can't compare with the first element in the array
46    * since it may be a category (without an identifier). */
47   for (bool is_first = true; item->identifier; item++) {
48     if (item->identifier[0]) {
49       BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier);
50       is_first = false;
51     }
52   }
53 
54   char *cstring = BLI_dynstr_get_cstring(dynstr);
55   BLI_dynstr_free(dynstr);
56   return cstring;
57 }
58 
BPy_reports_to_error(ReportList * reports,PyObject * exception,const bool clear)59 short BPy_reports_to_error(ReportList *reports, PyObject *exception, const bool clear)
60 {
61   char *report_str;
62 
63   report_str = BKE_reports_string(reports, RPT_ERROR);
64 
65   if (clear == true) {
66     BKE_reports_clear(reports);
67   }
68 
69   if (report_str) {
70     PyErr_SetString(exception, report_str);
71     MEM_freeN(report_str);
72   }
73 
74   return (report_str == NULL) ? 0 : -1;
75 }
76 
77 /**
78  * A version of #BKE_report_write_file_fp that uses Python's stdout.
79  */
BPy_reports_write_stdout(const ReportList * reports,const char * header)80 void BPy_reports_write_stdout(const ReportList *reports, const char *header)
81 {
82   if (header) {
83     PySys_WriteStdout("%s\n", header);
84   }
85 
86   LISTBASE_FOREACH (const Report *, report, &reports->list) {
87     PySys_WriteStdout("%s: %s\n", report->typestr, report->message);
88   }
89 }
90 
BPy_errors_to_report_ex(ReportList * reports,const char * error_prefix,const bool use_full,const bool use_location)91 bool BPy_errors_to_report_ex(ReportList *reports,
92                              const char *error_prefix,
93                              const bool use_full,
94                              const bool use_location)
95 {
96   PyObject *pystring;
97 
98   if (!PyErr_Occurred()) {
99     return 1;
100   }
101 
102   /* less hassle if we allow NULL */
103   if (reports == NULL) {
104     PyErr_Print();
105     PyErr_Clear();
106     return 1;
107   }
108 
109   if (use_full) {
110     pystring = PyC_ExceptionBuffer();
111   }
112   else {
113     pystring = PyC_ExceptionBuffer_Simple();
114   }
115 
116   if (pystring == NULL) {
117     BKE_report(reports, RPT_ERROR, "Unknown py-exception, could not convert");
118     return 0;
119   }
120 
121   if (error_prefix == NULL) {
122     /* Not very helpful, better than nothing. */
123     error_prefix = "Python";
124   }
125 
126   if (use_location) {
127     const char *filename;
128     int lineno;
129 
130     PyC_FileAndNum(&filename, &lineno);
131     if (filename == NULL) {
132       filename = "<unknown location>";
133     }
134 
135     BKE_reportf(reports,
136                 RPT_ERROR,
137                 TIP_("%s: %s\nlocation: %s:%d\n"),
138                 error_prefix,
139                 _PyUnicode_AsString(pystring),
140                 filename,
141                 lineno);
142 
143     /* Not exactly needed. Useful for developers tracking down issues. */
144     fprintf(stderr,
145             TIP_("%s: %s\nlocation: %s:%d\n"),
146             error_prefix,
147             _PyUnicode_AsString(pystring),
148             filename,
149             lineno);
150   }
151   else {
152     BKE_reportf(reports, RPT_ERROR, "%s: %s", error_prefix, _PyUnicode_AsString(pystring));
153   }
154 
155   Py_DECREF(pystring);
156   return 1;
157 }
158 
BPy_errors_to_report(ReportList * reports)159 bool BPy_errors_to_report(ReportList *reports)
160 {
161   return BPy_errors_to_report_ex(reports, NULL, true, true);
162 }
163