Files
test2/source/blender/python/intern/bpy_rna_context.cc
Campbell Barton 715a8268fa Build: resolve errors & deprecation warnings with Python 3.14 beta
Python 3.14 has moved some functionality into the public API,
use the updated names even with older Python versions.

Also resolve an error caused by variable reuse with delayed annotation
evaluation for TextureProperties_MixIn on startup.

Resolve #140695.
2025-06-20 14:34:25 +10:00

796 lines
26 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup pythonintern
*
* This file adds some helper methods to the context, that cannot fit well in RNA itself.
*/
#include <Python.h>
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "BKE_context.hh"
#include "BKE_main.hh"
#include "BKE_screen.hh"
#include "BKE_workspace.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "bpy_rna_context.hh"
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
#include "bpy_rna.hh"
/* -------------------------------------------------------------------- */
/** \name Private Utility Functions
* \{ */
static void bpy_rna_context_temp_set_screen_for_window(bContext *C, wmWindow *win, bScreen *screen)
{
if (screen == nullptr) {
return;
}
if (screen == WM_window_get_active_screen(win)) {
return;
}
WorkSpace *workspace;
BKE_workspace_layout_find_global(CTX_data_main(C), screen, &workspace);
/* Changing workspace instead of just screen as they are tied. */
WM_window_set_active_workspace(C, win, workspace);
WM_window_set_active_screen(win, workspace, screen);
}
/**
* Switching to or away from this screen is not supported.
*/
static bool wm_check_screen_switch_supported(const bScreen *screen)
{
if (screen->temp != 0) {
return false;
}
if (BKE_screen_is_fullscreen_area(screen)) {
return false;
}
return true;
}
static bool wm_check_window_exists(const Main *bmain, const wmWindow *win)
{
LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
if (BLI_findindex(&wm->windows, win) != -1) {
return true;
}
}
return false;
}
static bool wm_check_screen_exists(const Main *bmain, const bScreen *screen)
{
if (BLI_findindex(&bmain->screens, screen) != -1) {
return true;
}
return false;
}
static bool wm_check_area_exists(const wmWindow *win, const bScreen *screen, const ScrArea *area)
{
if (win && (BLI_findindex(&win->global_areas.areabase, area) != -1)) {
return true;
}
if (screen && (BLI_findindex(&screen->areabase, area) != -1)) {
return true;
}
return false;
}
static bool wm_check_region_exists(const bScreen *screen,
const ScrArea *area,
const ARegion *region)
{
if (screen && (BLI_findindex(&screen->regionbase, region) != -1)) {
return true;
}
if (area && (BLI_findindex(&area->regionbase, region) != -1)) {
return true;
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Temporary Context Override (Python Context Manager)
* \{ */
struct ContextStore {
wmWindow *win;
bool win_is_set;
bScreen *screen;
bool screen_is_set;
ScrArea *area;
bool area_is_set;
ARegion *region;
bool region_is_set;
};
struct BPyContextTempOverride {
PyObject_HEAD /* Required Python macro. */
bContext *context;
ContextStore ctx_init;
ContextStore ctx_temp;
struct {
/**
* The original screen of `ctx_temp.win`, needed when restoring this windows screen as it
* won't be `ctx_init.screen` (when switching the window as well as the screen), see #115937.
*/
bScreen *screen;
} ctx_temp_orig;
/** Bypass Python overrides set when calling an operator from Python. */
bContext_PyState py_state;
/**
* This dictionary is used to store members that don't have special handling,
* see: #bpy_context_temp_override_extract_known_args,
* these will then be accessed via #BPY_context_member_get.
*
* This also supports nested *stacking*, so a nested temp-context-overrides
* will overlay the new members on the old members (instead of ignoring them).
*/
PyObject *py_state_context_dict;
};
static void bpy_rna_context_temp_override_dealloc(BPyContextTempOverride *self)
{
PyObject_GC_UnTrack(self);
Py_CLEAR(self->py_state_context_dict);
PyObject_GC_Del(self);
}
static int bpy_rna_context_temp_override_traverse(BPyContextTempOverride *self,
visitproc visit,
void *arg)
{
Py_VISIT(self->py_state_context_dict);
return 0;
}
static int bpy_rna_context_temp_override_clear(BPyContextTempOverride *self)
{
Py_CLEAR(self->py_state_context_dict);
return 0;
}
static bool bpy_rna_context_temp_override_enter_ok_or_error(const BPyContextTempOverride *self,
const Main *bmain,
const wmWindow *win,
const bScreen *screen,
const ScrArea *area,
const ARegion *region)
{
/* NOTE(@ideasman42): Regarding sanity checks.
* There are 3 different situations to be accounted for here regarding overriding windowing data.
*
* - 1) Nothing is overridden.
* Simple, no sanity checks needed.
*
* - 2) Some members are overridden.
* Check the state is consistent (that the region is part the area or screen for example).
*
* - 3) Some members are overridden *but* the context members are unchanged.
* This is a less obvious case which often happens when a Python script copies the context
* typically via `context.copy()`, manipulates it and passes it in as keyword arguments.
*
* A naive approach could be to behave as if these arguments weren't passed in
* which would work in many situations however there is a difference
* since these members are used to restore the context afterwards.
* It's possible a script might use this context-manager to *pin* the context,
* running actions that change the context, relying on the context to be restored.
*
* When error-checking unchanged context members some error checks must be skipped
* such as the check to disallow temporary screens since that could break using
* `temp_override(..)` running with the current context from a render-window for example.
*
* In fact all sanity checks could be disabled when the members involved remain unchanged
* however it's possible Python scripts corrupt Blender's internal windowing state so keeping
* the checks is harmless and alerts developers early on that something is wrong.
*/
if (self->ctx_temp.region_is_set && (region != nullptr)) {
if (screen == nullptr && area == nullptr) {
PyErr_SetString(PyExc_TypeError, "Region set with screen & area set to None");
return false;
}
if (!wm_check_region_exists(screen, area, region)) {
PyErr_SetString(PyExc_TypeError, "Region not found in area or screen");
return false;
}
}
if (self->ctx_temp.area_is_set && (area != nullptr)) {
if (win == nullptr && screen == nullptr) {
PyErr_SetString(PyExc_TypeError, "Area set with window & screen set to None");
return false;
}
if (!wm_check_area_exists(win, screen, area)) {
PyErr_SetString(PyExc_TypeError, "Area not found in screen");
return false;
}
}
if (self->ctx_temp.screen_is_set && (screen != nullptr)) {
if (win == nullptr) {
PyErr_SetString(PyExc_TypeError, "Screen set with null window");
return false;
}
if (!wm_check_screen_exists(bmain, screen)) {
PyErr_SetString(PyExc_TypeError, "Screen not found");
return false;
}
/* Skip some checks when the screen is unchanged. */
if (self->ctx_init.screen_is_set) {
/* Switching away from a temporary screen isn't supported. */
if ((self->ctx_init.screen != nullptr) &&
!wm_check_screen_switch_supported(self->ctx_init.screen))
{
PyErr_SetString(PyExc_TypeError,
"Overriding context with an active temporary screen isn't supported");
return false;
}
if (!wm_check_screen_switch_supported(screen)) {
PyErr_SetString(PyExc_TypeError,
"Overriding context with temporary screen isn't supported");
return false;
}
if (BKE_workspace_layout_find_global(bmain, screen, nullptr) == nullptr) {
PyErr_SetString(PyExc_TypeError, "Screen has no workspace");
return false;
}
LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
if (win_iter == win) {
continue;
}
if (screen == WM_window_get_active_screen(win_iter)) {
PyErr_SetString(PyExc_TypeError, "Screen is used by another window");
return false;
}
}
}
}
}
if (self->ctx_temp.win_is_set && (win != nullptr)) {
if (!wm_check_window_exists(bmain, win)) {
PyErr_SetString(PyExc_TypeError, "Window not found");
return false;
}
}
return true;
}
static PyObject *bpy_rna_context_temp_override_enter(BPyContextTempOverride *self)
{
bContext *C = self->context;
Main *bmain = CTX_data_main(C);
/* It's crucial to call #CTX_py_state_pop if this function fails with an error. */
CTX_py_state_push(C, &self->py_state, self->py_state_context_dict);
self->ctx_init.win = CTX_wm_window(C);
self->ctx_init.screen = self->ctx_init.win ? WM_window_get_active_screen(self->ctx_init.win) :
CTX_wm_screen(C);
self->ctx_init.area = CTX_wm_area(C);
self->ctx_init.region = CTX_wm_region(C);
wmWindow *win = self->ctx_temp.win_is_set ? self->ctx_temp.win : self->ctx_init.win;
bScreen *screen = self->ctx_temp.screen_is_set ? self->ctx_temp.screen : self->ctx_init.screen;
ScrArea *area = self->ctx_temp.area_is_set ? self->ctx_temp.area : self->ctx_init.area;
ARegion *region = self->ctx_temp.region_is_set ? self->ctx_temp.region : self->ctx_init.region;
self->ctx_init.win_is_set = (self->ctx_init.win != win);
self->ctx_init.screen_is_set = (self->ctx_init.screen != screen);
self->ctx_init.area_is_set = (self->ctx_init.area != area);
self->ctx_init.region_is_set = (self->ctx_init.region != region);
/* When the screen isn't passed but a window is, match the screen to the window,
* it's important to do this after setting `self->ctx_init.screen_is_set` because the screen is
* *not* set, only the window, restoring the window will also restore its screen, see #116297. */
if ((self->ctx_temp.win_is_set == true) && (self->ctx_temp.screen_is_set == false)) {
screen = win ? WM_window_get_active_screen(win) : nullptr;
}
if (!bpy_rna_context_temp_override_enter_ok_or_error(self, bmain, win, screen, area, region)) {
CTX_py_state_pop(C, &self->py_state);
return nullptr;
}
/* Manipulate the context (setup). */
if (self->ctx_temp.screen_is_set) {
self->ctx_temp_orig.screen = WM_window_get_active_screen(win);
bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp.screen);
}
/* NOTE: always set these members, even when they are equal to the current values because
* setting the window (for example) clears the area & region, setting the area clears the region.
* While it would be useful in some cases to leave the context as-is when setting members
* to their current values.
*
* Favor predictable behavior, where setting a member *always* clears the nested
* values it contains - no matter the state of the current context.
* If this difference is important, the caller can always detect this case and avoid
* passing in the context override altogether. */
if (self->ctx_temp.win_is_set) {
CTX_wm_window_set(C, self->ctx_temp.win);
}
if (self->ctx_temp.screen_is_set) {
CTX_wm_screen_set(C, self->ctx_temp.screen);
}
if (self->ctx_temp.area_is_set) {
CTX_wm_area_set(C, self->ctx_temp.area);
}
if (self->ctx_temp.region_is_set) {
CTX_wm_region_set(C, self->ctx_temp.region);
}
Py_RETURN_NONE;
}
static PyObject *bpy_rna_context_temp_override_exit(BPyContextTempOverride *self,
PyObject * /*args*/)
{
bContext *C = self->context;
Main *bmain = CTX_data_main(C);
/* Manipulate the context (restore). */
if (self->ctx_temp.screen_is_set) {
if (self->ctx_temp_orig.screen && wm_check_screen_exists(bmain, self->ctx_temp_orig.screen)) {
wmWindow *win = self->ctx_temp.win_is_set ? self->ctx_temp.win : self->ctx_init.win;
if (win && wm_check_window_exists(bmain, win)) {
/* Disallow switching away from temporary-screens & full-screen areas, while it could be
* useful to support this closing a these screens uses different and more involved logic
* compared with switching between user managed screens, see: #117188. */
if (wm_check_screen_switch_supported(WM_window_get_active_screen(win))) {
bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp_orig.screen);
}
}
}
}
/* Account for the window to be freed on file-read,
* in this case the window should not be restored, see: #92818.
* Also account for other windowing members to be removed on exit,
* in this case the context is cleared. */
bool do_restore = true;
/* Restore context members as needed.
*
* The checks here behaves as follows:
* - When `self->ctx_init.win_is_set` is true, the window was changed by the override.
* in this case restore the initial window.
* - When `self->ctx_temp.win_is_set` is true, the window was set to the current value.
* Setting the window (even to the current value) must be accounted for
* because setting the window clears the area and the region members,
* which must now be restored.
*
* `is_container_set` is used to detect if nested context members need to be restored.
* The comments above refer to the window, it also applies to the screen containing an area
* and area which contains a region. */
bool is_container_set = false;
/* Handle Window. */
if (do_restore) {
if (self->ctx_init.win && !wm_check_window_exists(bmain, self->ctx_init.win)) {
CTX_wm_window_set(C, nullptr);
do_restore = false;
}
if (do_restore) {
if (self->ctx_init.win_is_set) {
CTX_wm_window_set(C, self->ctx_init.win);
is_container_set = true;
}
else if (self->ctx_temp.win_is_set) {
if (self->ctx_init.win == CTX_wm_window(C)) {
is_container_set = true;
}
else {
/* If the context changed, it's incorrect to attempt to restored nested members,
* in this case leave the context as-is, see: #119202. */
do_restore = false;
}
}
}
}
/* Handle Screen. */
if (do_restore) {
if (self->ctx_init.screen && !wm_check_screen_exists(bmain, self->ctx_init.screen)) {
CTX_wm_screen_set(C, nullptr);
do_restore = false;
}
if (do_restore) {
if (self->ctx_init.screen_is_set || is_container_set) {
CTX_wm_screen_set(C, self->ctx_init.screen);
is_container_set = true;
}
else if (self->ctx_temp.screen_is_set) {
if (self->ctx_init.screen == CTX_wm_screen(C)) {
is_container_set = true;
}
else {
do_restore = false;
}
}
}
}
/* Handle Area. */
if (do_restore) {
if (self->ctx_init.area &&
!wm_check_area_exists(self->ctx_init.win, self->ctx_init.screen, self->ctx_init.area))
{
CTX_wm_area_set(C, nullptr);
do_restore = false;
}
if (do_restore) {
if (self->ctx_init.area_is_set || is_container_set) {
CTX_wm_area_set(C, self->ctx_init.area);
is_container_set = true;
}
else if (self->ctx_temp.area_is_set) {
if (self->ctx_init.area == CTX_wm_area(C)) {
is_container_set = true;
}
else {
do_restore = false;
}
}
}
}
/* Handle Region. */
if (do_restore) {
if (self->ctx_init.region &&
!wm_check_region_exists(self->ctx_init.screen, self->ctx_init.area, self->ctx_init.region))
{
CTX_wm_region_set(C, nullptr);
do_restore = false;
}
if (do_restore) {
if (self->ctx_init.region_is_set || is_container_set) {
CTX_wm_region_set(C, self->ctx_init.region);
is_container_set = true;
}
/* Enable is there is ever data nested within the region. */
else if (false && self->ctx_temp.region_is_set) {
if (self->ctx_init.region == CTX_wm_region(C)) {
is_container_set = true;
}
else {
do_restore = false;
}
}
}
}
UNUSED_VARS(is_container_set, do_restore);
/* Finished restoring the context. */
/* A copy may have been made when writing context members, see #BPY_context_dict_clear_members */
PyObject *context_dict_test = static_cast<PyObject *>(CTX_py_dict_get(C));
if (context_dict_test && (context_dict_test != self->py_state_context_dict)) {
Py_DECREF(context_dict_test);
}
CTX_py_state_pop(C, &self->py_state);
Py_RETURN_NONE;
}
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
static PyMethodDef bpy_rna_context_temp_override_methods[] = {
{"__enter__", (PyCFunction)bpy_rna_context_temp_override_enter, METH_NOARGS},
{"__exit__", (PyCFunction)bpy_rna_context_temp_override_exit, METH_VARARGS},
{nullptr},
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
static PyTypeObject BPyContextTempOverride_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "ContextTempOverride",
/*tp_basicsize*/ sizeof(BPyContextTempOverride),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)bpy_rna_context_temp_override_dealloc,
/*tp_vectorcall_offset*/ 0,
/*tp_getattr*/ nullptr,
/*tp_setattr*/ nullptr,
/*tp_as_async*/ nullptr,
/*tp_repr*/ nullptr,
/*tp_as_number*/ nullptr,
/*tp_as_sequence*/ nullptr,
/*tp_as_mapping*/ nullptr,
/*tp_hash*/ nullptr,
/*tp_call*/ nullptr,
/*tp_str*/ nullptr,
/*tp_getattro*/ nullptr,
/*tp_setattro*/ nullptr,
/*tp_as_buffer*/ nullptr,
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
/*tp_doc*/ nullptr,
/*tp_traverse*/ (traverseproc)bpy_rna_context_temp_override_traverse,
/*tp_clear*/ (inquiry)bpy_rna_context_temp_override_clear,
/*tp_richcompare*/ nullptr,
/*tp_weaklistoffset*/ 0,
/*tp_iter*/ nullptr,
/*tp_iternext*/ nullptr,
/*tp_methods*/ bpy_rna_context_temp_override_methods,
/*tp_members*/ nullptr,
/*tp_getset*/ nullptr,
/*tp_base*/ nullptr,
/*tp_dict*/ nullptr,
/*tp_descr_get*/ nullptr,
/*tp_descr_set*/ nullptr,
/*tp_dictoffset*/ 0,
/*tp_init*/ nullptr,
/*tp_alloc*/ nullptr,
/*tp_new*/ nullptr,
/*tp_free*/ nullptr,
/*tp_is_gc*/ nullptr,
/*tp_bases*/ nullptr,
/*tp_mro*/ nullptr,
/*tp_cache*/ nullptr,
/*tp_subclasses*/ nullptr,
/*tp_weaklist*/ nullptr,
/*tp_del*/ nullptr,
/*tp_version_tag*/ 0,
/*tp_finalize*/ nullptr,
/*tp_vectorcall*/ nullptr,
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Context Temporary Override Method
* \{ */
static PyObject *bpy_context_temp_override_extract_known_args(const char *const *kwds_static,
PyObject *kwds)
{
PyObject *sentinel = Py_Ellipsis;
PyObject *kwds_parse = PyDict_New();
for (int i = 0; kwds_static[i]; i++) {
PyObject *key = PyUnicode_FromString(kwds_static[i]);
PyObject *val = _PyDict_Pop(kwds, key, sentinel);
if (val != sentinel) {
if (PyDict_SetItem(kwds_parse, key, val) == -1) {
BLI_assert_unreachable();
}
}
Py_DECREF(key);
Py_DECREF(val);
}
return kwds_parse;
}
/* NOTE(@ideasman42): `ContextTempOverride` isn't accessible from (without creating an instance),
* it should be exposed although it doesn't seem especially important either. */
PyDoc_STRVAR(
/* Wrap. */
bpy_context_temp_override_doc,
".. method:: temp_override(*, window=None, area=None, region=None, **keywords)\n"
"\n"
" Context manager to temporarily override members in the context.\n"
"\n"
" :arg window: Window override or None.\n"
" :type window: :class:`bpy.types.Window`\n"
" :arg screen: Screen override or None.\n"
"\n"
" .. note:: Switching to or away from full-screen areas & temporary screens "
"isn't supported. Passing in these screens will raise an exception, "
"actions that leave the context such screens won't restore the prior screen.\n"
"\n"
" .. note:: Changing the screen has wider implications "
"than other arguments as it will also change the works-space "
"and potentially the scene (when pinned).\n"
"\n"
" :type screen: :class:`bpy.types.Screen`\n"
" :arg area: Area override or None.\n"
" :type area: :class:`bpy.types.Area`\n"
" :arg region: Region override or None.\n"
" :type region: :class:`bpy.types.Region`\n"
" :arg keywords: Additional keywords override context members.\n"
" :return: The context manager .\n"
" :rtype: ContextTempOverride\n");
static PyObject *bpy_context_temp_override(PyObject *self, PyObject *args, PyObject *kwds)
{
const PointerRNA *context_ptr = pyrna_struct_as_ptr(self, &RNA_Context);
if (context_ptr == nullptr) {
return nullptr;
}
if (kwds == nullptr) {
/* While this is effectively NOP, support having no keywords as it's more involved
* to return an alternative (dummy) context manager. */
}
else {
/* Needed because the keywords copied into `kwds_parse` could contain anything.
* As the types of keys aren't checked. */
if (!PyArg_ValidateKeywordArguments(kwds)) {
return nullptr;
}
}
struct {
BPy_StructRNA_Parse window;
BPy_StructRNA_Parse screen;
BPy_StructRNA_Parse area;
BPy_StructRNA_Parse region;
} params{};
params.window.type = &RNA_Window;
params.screen.type = &RNA_Screen;
params.area.type = &RNA_Area;
params.region.type = &RNA_Region;
static const char *const _keywords[] = {
"window",
"screen",
"area",
"region",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"|$" /* Optional, keyword only arguments. */
"O&" /* `window` */
"O&" /* `screen` */
"O&" /* `area` */
"O&" /* `region` */
":temp_override",
_keywords,
nullptr,
};
/* Parse known keywords, the remaining keywords are set using #CTX_py_state_push. */
kwds = kwds ? PyDict_Copy(kwds) : PyDict_New();
{
PyObject *kwds_parse = bpy_context_temp_override_extract_known_args(_keywords, kwds);
const int parse_result = _PyArg_ParseTupleAndKeywordsFast(args,
kwds_parse,
&_parser,
pyrna_struct_as_ptr_or_null_parse,
&params.window,
pyrna_struct_as_ptr_or_null_parse,
&params.screen,
pyrna_struct_as_ptr_or_null_parse,
&params.area,
pyrna_struct_as_ptr_or_null_parse,
&params.region);
Py_DECREF(kwds_parse);
if (!parse_result) {
Py_DECREF(kwds);
return nullptr;
}
}
bContext *C = static_cast<bContext *>(context_ptr->data);
{
/* Merge existing keys that don't exist in the keywords passed in.
* This makes it possible to nest context overrides. */
PyObject *context_dict_current = static_cast<PyObject *>(CTX_py_dict_get(C));
if (context_dict_current != nullptr) {
PyDict_Merge(kwds, context_dict_current, 0);
}
}
ContextStore ctx_temp = {nullptr};
if (params.window.ptr != nullptr) {
ctx_temp.win = static_cast<wmWindow *>(params.window.ptr->data);
ctx_temp.win_is_set = true;
}
if (params.screen.ptr != nullptr) {
ctx_temp.screen = static_cast<bScreen *>(params.screen.ptr->data);
ctx_temp.screen_is_set = true;
}
if (params.area.ptr != nullptr) {
ctx_temp.area = static_cast<ScrArea *>(params.area.ptr->data);
ctx_temp.area_is_set = true;
}
if (params.region.ptr != nullptr) {
ctx_temp.region = static_cast<ARegion *>(params.region.ptr->data);
ctx_temp.region_is_set = true;
}
BPyContextTempOverride *ret = PyObject_GC_New(BPyContextTempOverride,
&BPyContextTempOverride_Type);
ret->context = C;
ret->ctx_temp = ctx_temp;
memset(&ret->ctx_init, 0, sizeof(ret->ctx_init));
ret->ctx_temp_orig.screen = nullptr;
ret->py_state_context_dict = kwds;
PyObject_GC_Track(ret);
return (PyObject *)ret;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Public Type Definition
* \{ */
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
PyMethodDef BPY_rna_context_temp_override_method_def = {
"temp_override",
(PyCFunction)bpy_context_temp_override,
METH_VARARGS | METH_KEYWORDS,
bpy_context_temp_override_doc,
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif
void bpy_rna_context_types_init()
{
if (PyType_Ready(&BPyContextTempOverride_Type) < 0) {
BLI_assert_unreachable();
return;
}
}
/** \} */