2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2021-03-09 01:01:31 +11:00
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
|
* \ingroup pythonintern
|
|
|
|
|
*
|
|
|
|
|
* This file defines the API to support temporarily creating #Main data.
|
|
|
|
|
* The only use case for this is currently to support temporarily loading data-blocks
|
|
|
|
|
* which can be freed, without them polluting the current #G_MAIN.
|
|
|
|
|
*
|
|
|
|
|
* This is exposed via a context manager `bpy.types.BlendData.temp_data(...)`
|
|
|
|
|
* which returns a new `bpy.types.BlendData` that is freed once the context manager exits.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <Python.h>
|
2023-07-21 16:05:33 +10:00
|
|
|
#include <cstddef>
|
2021-03-09 01:01:31 +11:00
|
|
|
|
2024-09-24 15:25:36 +02:00
|
|
|
#include "../generic/py_capi_utils.hh"
|
|
|
|
|
#include "../generic/python_compat.hh"
|
2023-08-11 14:59:55 +10:00
|
|
|
|
2021-03-09 01:01:31 +11:00
|
|
|
#include "BLI_string.h"
|
|
|
|
|
|
2024-02-10 18:25:14 +01:00
|
|
|
#include "BKE_global.hh"
|
2023-12-01 19:43:16 +01:00
|
|
|
#include "BKE_main.hh"
|
2021-03-09 01:01:31 +11:00
|
|
|
|
2023-08-10 22:40:27 +02:00
|
|
|
#include "RNA_access.hh"
|
2024-07-10 18:30:02 +02:00
|
|
|
#include "RNA_prototypes.hh"
|
2021-03-09 01:01:31 +11:00
|
|
|
|
2024-09-24 11:30:38 +02:00
|
|
|
#include "bpy_rna.hh"
|
|
|
|
|
#include "bpy_rna_data.hh"
|
2021-03-09 01:01:31 +11:00
|
|
|
|
2023-07-21 02:18:59 +02:00
|
|
|
struct BPy_DataContext {
|
2021-06-24 15:56:58 +10:00
|
|
|
PyObject_HEAD /* Required Python macro. */
|
2021-06-24 17:10:22 +10:00
|
|
|
BPy_StructRNA *data_rna;
|
2021-03-09 01:01:31 +11:00
|
|
|
char filepath[1024];
|
2023-07-21 02:18:59 +02:00
|
|
|
};
|
2021-03-09 01:01:31 +11:00
|
|
|
|
2024-07-27 13:20:43 +10:00
|
|
|
static PyObject *bpy_rna_data_temp_data(PyObject *self, PyObject *args, PyObject *kw);
|
2021-03-09 01:01:31 +11:00
|
|
|
static PyObject *bpy_rna_data_context_enter(BPy_DataContext *self);
|
|
|
|
|
static PyObject *bpy_rna_data_context_exit(BPy_DataContext *self, PyObject *args);
|
|
|
|
|
|
2023-07-21 13:42:35 +10:00
|
|
|
#if (defined(__GNUC__) && !defined(__clang__))
|
|
|
|
|
# pragma GCC diagnostic push
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-09 01:01:31 +11:00
|
|
|
static PyMethodDef bpy_rna_data_context_methods[] = {
|
|
|
|
|
{"__enter__", (PyCFunction)bpy_rna_data_context_enter, METH_NOARGS},
|
|
|
|
|
{"__exit__", (PyCFunction)bpy_rna_data_context_exit, METH_VARARGS},
|
2023-07-21 02:18:59 +02:00
|
|
|
{nullptr} /* sentinel */
|
2021-03-09 01:01:31 +11:00
|
|
|
};
|
|
|
|
|
|
2023-07-21 13:42:35 +10:00
|
|
|
#if (defined(__GNUC__) && !defined(__clang__))
|
|
|
|
|
# pragma GCC diagnostic pop
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-09 01:01:31 +11:00
|
|
|
static int bpy_rna_data_context_traverse(BPy_DataContext *self, visitproc visit, void *arg)
|
|
|
|
|
{
|
|
|
|
|
Py_VISIT(self->data_rna);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int bpy_rna_data_context_clear(BPy_DataContext *self)
|
|
|
|
|
{
|
|
|
|
|
Py_CLEAR(self->data_rna);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void bpy_rna_data_context_dealloc(BPy_DataContext *self)
|
|
|
|
|
{
|
|
|
|
|
PyObject_GC_UnTrack(self);
|
|
|
|
|
Py_CLEAR(self->data_rna);
|
|
|
|
|
PyObject_GC_Del(self);
|
|
|
|
|
}
|
|
|
|
|
static PyTypeObject bpy_rna_data_context_Type = {
|
2023-07-21 02:18:59 +02:00
|
|
|
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
2022-11-07 22:34:35 +11:00
|
|
|
/*tp_name*/ "bpy_rna_data_context",
|
|
|
|
|
/*tp_basicsize*/ sizeof(BPy_DataContext),
|
|
|
|
|
/*tp_itemsize*/ 0,
|
|
|
|
|
/*tp_dealloc*/ (destructor)bpy_rna_data_context_dealloc,
|
|
|
|
|
/*tp_vectorcall_offset*/ 0,
|
2023-07-21 02:18:59 +02:00
|
|
|
/*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,
|
2022-11-07 22:34:35 +11:00
|
|
|
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
2023-07-21 02:18:59 +02:00
|
|
|
/*tp_doc*/ nullptr,
|
2022-11-07 22:34:35 +11:00
|
|
|
/*tp_traverse*/ (traverseproc)bpy_rna_data_context_traverse,
|
|
|
|
|
/*tp_clear*/ (inquiry)bpy_rna_data_context_clear,
|
2023-07-21 02:18:59 +02:00
|
|
|
/*tp_richcompare*/ nullptr,
|
2022-11-07 22:34:35 +11:00
|
|
|
/*tp_weaklistoffset*/ 0,
|
2023-07-21 02:18:59 +02:00
|
|
|
/*tp_iter*/ nullptr,
|
|
|
|
|
/*tp_iternext*/ nullptr,
|
2022-11-07 22:34:35 +11:00
|
|
|
/*tp_methods*/ bpy_rna_data_context_methods,
|
2023-07-21 02:18:59 +02:00
|
|
|
/*tp_members*/ nullptr,
|
|
|
|
|
/*tp_getset*/ nullptr,
|
|
|
|
|
/*tp_base*/ nullptr,
|
|
|
|
|
/*tp_dict*/ nullptr,
|
|
|
|
|
/*tp_descr_get*/ nullptr,
|
|
|
|
|
/*tp_descr_set*/ nullptr,
|
2022-11-07 22:34:35 +11:00
|
|
|
/*tp_dictoffset*/ 0,
|
2023-07-21 02:18:59 +02:00
|
|
|
/*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,
|
2022-11-07 22:34:35 +11:00
|
|
|
/*tp_version_tag*/ 0,
|
2023-07-21 02:18:59 +02:00
|
|
|
/*tp_finalize*/ nullptr,
|
|
|
|
|
/*tp_vectorcall*/ nullptr,
|
2021-03-09 01:01:31 +11:00
|
|
|
};
|
|
|
|
|
|
2024-01-25 10:22:16 +11:00
|
|
|
PyDoc_STRVAR(
|
|
|
|
|
/* Wrap. */
|
|
|
|
|
bpy_rna_data_context_load_doc,
|
|
|
|
|
".. method:: temp_data(filepath=None)\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" A context manager that temporarily creates blender file data.\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" :arg filepath: The file path for the newly temporary data. "
|
|
|
|
|
"When None, the path of the currently open file is used.\n"
|
2024-11-03 15:42:19 +11:00
|
|
|
" :type filepath: str | bytes | None\n"
|
2024-01-25 10:22:16 +11:00
|
|
|
"\n"
|
|
|
|
|
" :return: Blend file data which is freed once the context exists.\n"
|
|
|
|
|
" :rtype: :class:`bpy.types.BlendData`\n");
|
2021-03-09 01:01:31 +11:00
|
|
|
|
2023-07-21 02:18:59 +02:00
|
|
|
static PyObject *bpy_rna_data_temp_data(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
2021-03-09 01:01:31 +11:00
|
|
|
{
|
2023-08-11 14:59:55 +10:00
|
|
|
PyC_UnicodeAsBytesAndSize_Data filepath_data = {nullptr};
|
2021-03-09 01:01:31 +11:00
|
|
|
BPy_DataContext *ret;
|
2023-07-21 02:18:59 +02:00
|
|
|
static const char *_keywords[] = {"filepath", nullptr};
|
2022-04-08 09:41:28 +10:00
|
|
|
static _PyArg_Parser _parser = {
|
2023-08-30 14:05:32 +10:00
|
|
|
PY_ARG_PARSER_HEAD_COMPAT()
|
2022-04-08 09:41:28 +10:00
|
|
|
"|$" /* Optional keyword only arguments. */
|
2023-08-11 14:59:55 +10:00
|
|
|
"O&" /* `filepath` */
|
2022-04-08 09:41:28 +10:00
|
|
|
":temp_data",
|
|
|
|
|
_keywords,
|
2023-08-03 19:14:53 +10:00
|
|
|
nullptr,
|
2022-04-08 09:41:28 +10:00
|
|
|
};
|
2023-08-11 14:59:55 +10:00
|
|
|
if (!_PyArg_ParseTupleAndKeywordsFast(
|
|
|
|
|
args, kw, &_parser, PyC_ParseUnicodeAsBytesAndSize_OrNone, &filepath_data))
|
|
|
|
|
{
|
2023-07-21 02:18:59 +02:00
|
|
|
return nullptr;
|
2021-03-09 01:01:31 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = PyObject_GC_New(BPy_DataContext, &bpy_rna_data_context_Type);
|
|
|
|
|
|
2023-08-11 14:59:55 +10:00
|
|
|
STRNCPY(ret->filepath, filepath_data.value ? filepath_data.value : G_MAIN->filepath);
|
|
|
|
|
Py_XDECREF(filepath_data.value_coerce);
|
2021-03-09 01:01:31 +11:00
|
|
|
|
|
|
|
|
return (PyObject *)ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PyObject *bpy_rna_data_context_enter(BPy_DataContext *self)
|
|
|
|
|
{
|
|
|
|
|
Main *bmain_temp = BKE_main_new();
|
2025-01-24 16:45:32 +01:00
|
|
|
PointerRNA ptr = RNA_pointer_create_discrete(nullptr, &RNA_BlendData, bmain_temp);
|
2021-03-09 01:01:31 +11:00
|
|
|
|
|
|
|
|
self->data_rna = (BPy_StructRNA *)pyrna_struct_CreatePyObject(&ptr);
|
|
|
|
|
|
2022-09-28 16:09:12 +10:00
|
|
|
BLI_assert(!PyObject_GC_IsTracked((PyObject *)self));
|
2021-03-09 01:01:31 +11:00
|
|
|
PyObject_GC_Track(self);
|
|
|
|
|
|
|
|
|
|
return (PyObject *)self->data_rna;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-21 02:18:59 +02:00
|
|
|
static PyObject *bpy_rna_data_context_exit(BPy_DataContext *self, PyObject * /*args*/)
|
2021-03-09 01:01:31 +11:00
|
|
|
{
|
RNA: Make the `PointerRNA` struct non-trivial.
For now, PointerRNA is made non-trivial by giving explicit default
values to its members.
Besides of BPY python binding code, the change is relatively trivial.
The main change (besides the creation/deletion part) is the replacement
of `memset` by zero-initialized assignment (using `{}`).
makesrna required changes are quite small too.
The big piece of this PR is the refactor of the BPY RNA code.
It essentially brings back allocation and deletion of the BPy_StructRNA,
BPy_Pointer etc. python objects into 'cannonical process', using `__new__`,
and `__init__` callbacks (and there matching CAPI functions).
Existing code was doing very low-level manipulations to create these
data, which is not really easy to understand, and AFAICT incompatible
with handling C++ data that needs to be constructed and destructed.
Unfortunately, similar change in destruction code (using `__del__` and
matching `tp_finalize` CAPI callback) is not possible, because of technical
low-level implementation details in CPython (see [1] for details).
`std::optional` pointer management is used to encapsulate PointerRNA
data. This allows to keep control on _when_ actual RNA creation is done,
and to have a safe destruction in `tp_dealloc` callbacks.
Note that a critical change in Blender's Python API will be that classes
inherinting from `bpy_struct` etc. will now have to properly call the
base class `__new__` and/or `__init__`if they define them.
Implements #122431.
[1] https://discuss.python.org/t/cpython-usage-of-tp-finalize-in-c-defined-static-types-with-no-custom-tp-dealloc/64100
2024-10-30 15:08:37 +01:00
|
|
|
BKE_main_free(static_cast<Main *>(self->data_rna->ptr->data));
|
2025-02-18 12:31:20 +01:00
|
|
|
self->data_rna->ptr->invalidate();
|
2021-03-09 01:01:31 +11:00
|
|
|
Py_RETURN_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-21 13:42:35 +10:00
|
|
|
#if (defined(__GNUC__) && !defined(__clang__))
|
|
|
|
|
# pragma GCC diagnostic push
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-03-09 01:01:31 +11:00
|
|
|
PyMethodDef BPY_rna_data_context_method_def = {
|
|
|
|
|
"temp_data",
|
|
|
|
|
(PyCFunction)bpy_rna_data_temp_data,
|
|
|
|
|
METH_STATIC | METH_VARARGS | METH_KEYWORDS,
|
|
|
|
|
bpy_rna_data_context_load_doc,
|
|
|
|
|
};
|
|
|
|
|
|
2023-07-21 13:42:35 +10:00
|
|
|
#if (defined(__GNUC__) && !defined(__clang__))
|
|
|
|
|
# pragma GCC diagnostic pop
|
|
|
|
|
#endif
|
|
|
|
|
|
2023-07-21 10:59:54 +10:00
|
|
|
int BPY_rna_data_context_type_ready()
|
2021-03-09 01:01:31 +11:00
|
|
|
{
|
|
|
|
|
if (PyType_Ready(&bpy_rna_data_context_Type) < 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|