RNA: support for marking properties as deprecated
Deprecation meta-data support for RNA properties. - Properties may have a deprecated note and version. - Warnings shown when these are accessed from Python. - A note is included in the generated documentation. Support for marking functions as deprecated can be added in the future. Ref !139487
This commit is contained in:
@@ -1338,6 +1338,8 @@ def pycontext2sphinx(basepath):
|
||||
fw(".. data:: {:s}\n\n".format(prop.identifier))
|
||||
if prop.description:
|
||||
fw(" {:s}\n\n".format(prop.description))
|
||||
if (deprecated := prop.deprecated) is not None:
|
||||
fw(pyrna_deprecated_directive(" ", deprecated))
|
||||
|
||||
# Special exception, can't use generic code here for enums.
|
||||
if prop.type == "enum":
|
||||
@@ -1449,6 +1451,23 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False):
|
||||
return ""
|
||||
|
||||
|
||||
def pyrna_deprecated_directive(ident, deprecated):
|
||||
note, version, removal_version = deprecated
|
||||
|
||||
# Show a short 2 number version where possible to reduce noise.
|
||||
version_str = "{:d}.{:d}.{:d}".format(*version).removesuffix(".0")
|
||||
removal_version_str = "{:d}.{:d}.{:d}".format(*removal_version).removesuffix(".0")
|
||||
|
||||
return (
|
||||
"{:s}.. deprecated:: {:s} removal planned in version {:s}\n"
|
||||
"\n"
|
||||
"{:s} {:s}\n"
|
||||
).format(
|
||||
ident, version_str, removal_version_str,
|
||||
ident, note,
|
||||
)
|
||||
|
||||
|
||||
def pyrna2sphinx(basepath):
|
||||
"""
|
||||
``bpy.types`` and ``bpy.ops``.
|
||||
@@ -1640,6 +1659,9 @@ def pyrna2sphinx(basepath):
|
||||
if prop.description:
|
||||
write_indented_lines(" ", fw, prop.description, False)
|
||||
fw("\n")
|
||||
if (deprecated := prop.deprecated) is not None:
|
||||
fw(pyrna_deprecated_directive(" ", deprecated))
|
||||
fw("\n")
|
||||
|
||||
# Special exception, can't use generic code here for enums.
|
||||
if prop.type == "enum":
|
||||
@@ -1719,6 +1741,10 @@ def pyrna2sphinx(basepath):
|
||||
prop.identifier,
|
||||
", ".join((val for val in (descr, type_descr) if val))
|
||||
))
|
||||
if (deprecated := prop.deprecated) is not None:
|
||||
fw(pyrna_deprecated_directive(" ", deprecated))
|
||||
fw("\n")
|
||||
|
||||
fw(" :rtype: ({:s})\n".format(", ".join(type_descrs)))
|
||||
|
||||
write_example_ref(" ", fw, struct_module_name + "." + struct_id + "." + func.identifier)
|
||||
|
||||
@@ -279,6 +279,7 @@ class InfoPropertyRNA:
|
||||
"is_never_none",
|
||||
"is_path_supports_blend_relative",
|
||||
"is_path_supports_templates",
|
||||
"deprecated",
|
||||
)
|
||||
global_lookup = {}
|
||||
|
||||
@@ -306,6 +307,15 @@ class InfoPropertyRNA:
|
||||
self.is_path_supports_blend_relative = rna_prop.is_path_supports_blend_relative
|
||||
self.is_path_supports_templates = rna_prop.is_path_supports_templates
|
||||
|
||||
if rna_prop.is_deprecated:
|
||||
self.deprecated = (
|
||||
rna_prop.deprecated_note,
|
||||
tuple(rna_prop.deprecated_version),
|
||||
tuple(rna_prop.deprecated_removal_version),
|
||||
)
|
||||
else:
|
||||
self.deprecated = None
|
||||
|
||||
self.type = rna_prop.type.lower()
|
||||
fixed_type = getattr(rna_prop, "fixed_type", "")
|
||||
if fixed_type:
|
||||
|
||||
@@ -239,6 +239,8 @@ bool RNA_struct_bl_idname_ok_or_report(ReportList *reports,
|
||||
const char *RNA_property_identifier(const PropertyRNA *prop);
|
||||
const char *RNA_property_description(PropertyRNA *prop);
|
||||
|
||||
const DeprecatedRNA *RNA_property_deprecated(const PropertyRNA *prop);
|
||||
|
||||
PropertyType RNA_property_type(PropertyRNA *prop);
|
||||
PropertySubType RNA_property_subtype(PropertyRNA *prop);
|
||||
PropertyUnit RNA_property_unit(PropertyRNA *prop);
|
||||
|
||||
@@ -450,6 +450,12 @@ void RNA_def_property_enum_default(PropertyRNA *prop, int value);
|
||||
void RNA_def_property_string_default(PropertyRNA *prop, const char *value);
|
||||
|
||||
void RNA_def_property_ui_text(PropertyRNA *prop, const char *name, const char *description);
|
||||
|
||||
void RNA_def_property_deprecated(PropertyRNA *prop,
|
||||
const char *note,
|
||||
short version,
|
||||
short removal_version);
|
||||
|
||||
/**
|
||||
* The values hare are a little confusing:
|
||||
*
|
||||
|
||||
@@ -938,6 +938,25 @@ struct ExtensionRNA {
|
||||
StructFreeFunc free;
|
||||
};
|
||||
|
||||
/**
|
||||
* Information about deprecated properties.
|
||||
*
|
||||
* Used by the API documentation and Python API to print warnings
|
||||
* when accessing a deprecated property.
|
||||
*/
|
||||
struct DeprecatedRNA {
|
||||
/** Single line deprecation message, suggest alternatives where possible. */
|
||||
const char *note;
|
||||
/** The released version this was deprecated. */
|
||||
short version;
|
||||
/**
|
||||
* The version this will be removed.
|
||||
* The value represents major, minor versions (sub-version isn't supported).
|
||||
* Compatible with #Main::versionfile (e.g. `502` for `v5.2`).
|
||||
*/
|
||||
short removal_version;
|
||||
};
|
||||
|
||||
/* Primitive types. */
|
||||
|
||||
struct PrimitiveStringRNA {
|
||||
|
||||
@@ -4130,6 +4130,17 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
|
||||
freenest = true;
|
||||
}
|
||||
|
||||
if (prop->deprecated) {
|
||||
fprintf(f,
|
||||
"static const DeprecatedRNA rna_%s%s_%s_deprecated = {\n\t",
|
||||
srna->identifier,
|
||||
strnest,
|
||||
prop->identifier);
|
||||
rna_print_c_string(f, prop->deprecated->note);
|
||||
fprintf(f, ",\n\t%d, %d,\n", prop->deprecated->version, prop->deprecated->removal_version);
|
||||
fprintf(f, "};\n\n");
|
||||
}
|
||||
|
||||
switch (prop->type) {
|
||||
case PROP_ENUM: {
|
||||
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
|
||||
@@ -4369,7 +4380,15 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
|
||||
fprintf(f, ",\n\t");
|
||||
fprintf(f, "%d, ", prop->icon);
|
||||
rna_print_c_string(f, prop->translation_context);
|
||||
fprintf(f, ",\n");
|
||||
fprintf(f, ",\n\t");
|
||||
|
||||
if (prop->deprecated) {
|
||||
fprintf(f, "&rna_%s%s_%s_deprecated,", srna->identifier, strnest, prop->identifier);
|
||||
}
|
||||
else {
|
||||
fprintf(f, "nullptr,\n");
|
||||
}
|
||||
|
||||
fprintf(f,
|
||||
"\t%s, PropertySubType(int(%s) | int(%s)), %s, %u, {%u, %u, %u}, %u,\n",
|
||||
RNA_property_typename(prop->type),
|
||||
|
||||
@@ -1237,6 +1237,11 @@ const char *RNA_property_description(PropertyRNA *prop)
|
||||
return TIP_(rna_ensure_property_description(prop));
|
||||
}
|
||||
|
||||
const DeprecatedRNA *RNA_property_deprecated(const PropertyRNA *prop)
|
||||
{
|
||||
return prop->deprecated;
|
||||
}
|
||||
|
||||
PropertyType RNA_property_type(PropertyRNA *prop)
|
||||
{
|
||||
return rna_ensure_property(prop)->type;
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_blender_version.h" /* For #BLENDER_VERSION deprecation warnings. */
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "UI_interface.hh" /* For things like UI_PRECISION_FLOAT_MAX... */
|
||||
@@ -1477,6 +1479,7 @@ PropertyRNA *RNA_def_property(StructOrFunctionRNA *cont_,
|
||||
prop->subtype = PropertySubType(subtype);
|
||||
prop->name = identifier;
|
||||
prop->description = "";
|
||||
prop->deprecated = nullptr;
|
||||
prop->translation_context = BLT_I18NCONTEXT_DEFAULT_BPYRNA;
|
||||
/* a priori not raw editable */
|
||||
prop->rawtype = RawPropertyType(-1);
|
||||
@@ -1749,6 +1752,47 @@ void RNA_def_property_ui_text(PropertyRNA *prop, const char *name, const char *d
|
||||
prop->description = description;
|
||||
}
|
||||
|
||||
void RNA_def_property_deprecated(PropertyRNA *prop,
|
||||
const char *note,
|
||||
const short version,
|
||||
const short removal_version)
|
||||
{
|
||||
if (!DefRNA.preprocess) {
|
||||
fprintf(stderr, "%s: \"%s\": only during preprocessing.", __func__, prop->identifier);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef RNA_RUNTIME
|
||||
StructRNA *srna = DefRNA.laststruct;
|
||||
BLI_assert(prop->deprecated == nullptr);
|
||||
BLI_assert(note != nullptr);
|
||||
BLI_assert(version > 0);
|
||||
BLI_assert(removal_version > version);
|
||||
|
||||
/* This message is to alert developers of deprecation
|
||||
* without breaking the build after a version bump. */
|
||||
if (removal_version <= BLENDER_VERSION) {
|
||||
fprintf(stderr,
|
||||
"\nWARNING: \"%s.%s\" deprecation starting at %d.%d marks this property to be removed "
|
||||
"in the current Blender version!\n\n",
|
||||
srna->identifier,
|
||||
prop->identifier,
|
||||
version / 100,
|
||||
version % 100);
|
||||
}
|
||||
|
||||
DeprecatedRNA *deprecated = static_cast<DeprecatedRNA *>(rna_calloc(sizeof(DeprecatedRNA)));
|
||||
deprecated->note = note;
|
||||
deprecated->version = version;
|
||||
deprecated->removal_version = removal_version;
|
||||
prop->deprecated = deprecated;
|
||||
#else
|
||||
(void)note;
|
||||
(void)version;
|
||||
(void)removal_version;
|
||||
#endif
|
||||
}
|
||||
|
||||
void RNA_def_property_ui_icon(PropertyRNA *prop, int icon, int consecutive)
|
||||
{
|
||||
prop->icon = icon;
|
||||
@@ -5082,6 +5126,9 @@ void RNA_def_property_free_pointers(PropertyRNA *prop)
|
||||
if (prop->py_data) {
|
||||
MEM_freeN(prop->py_data);
|
||||
}
|
||||
if (prop->deprecated) {
|
||||
MEM_freeN(prop->deprecated);
|
||||
}
|
||||
|
||||
switch (prop->type) {
|
||||
case PROP_BOOLEAN: {
|
||||
|
||||
@@ -368,6 +368,9 @@ struct PropertyRNA {
|
||||
/** Context for translation. */
|
||||
const char *translation_context;
|
||||
|
||||
/** Optional deprecation information. */
|
||||
const DeprecatedRNA *deprecated;
|
||||
|
||||
/** Property type as it appears to the outside. */
|
||||
PropertyType type;
|
||||
/** Subtype, 'interpretation' of the property. */
|
||||
|
||||
@@ -886,6 +886,50 @@ static bool rna_Property_is_runtime_get(PointerRNA *ptr)
|
||||
return RNA_property_is_runtime(prop);
|
||||
}
|
||||
|
||||
static bool rna_Property_is_deprecated_get(PointerRNA *ptr)
|
||||
{
|
||||
const PropertyRNA *prop = (const PropertyRNA *)ptr->data;
|
||||
return RNA_property_deprecated(prop) != nullptr;
|
||||
}
|
||||
|
||||
static int rna_Property_deprecated_note_length(PointerRNA *ptr)
|
||||
{
|
||||
const PropertyRNA *prop = (const PropertyRNA *)ptr->data;
|
||||
if (const DeprecatedRNA *deprecated = RNA_property_deprecated(prop)) {
|
||||
return strlen(deprecated->note);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rna_Property_deprecated_note_get(PointerRNA *ptr, char *value)
|
||||
{
|
||||
const PropertyRNA *prop = (const PropertyRNA *)ptr->data;
|
||||
if (const DeprecatedRNA *deprecated = RNA_property_deprecated(prop)) {
|
||||
strcpy(value, deprecated->note);
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_Property_deprecated_version_get(PointerRNA *ptr, int *value)
|
||||
{
|
||||
const PropertyRNA *prop = (const PropertyRNA *)ptr->data;
|
||||
short version = 0;
|
||||
if (const DeprecatedRNA *deprecated = RNA_property_deprecated(prop)) {
|
||||
version = deprecated->version;
|
||||
}
|
||||
ARRAY_SET_ITEMS(value, version / 100, version % 100, 0);
|
||||
}
|
||||
|
||||
static void rna_Property_deprecated_removal_version_get(PointerRNA *ptr, int *value)
|
||||
{
|
||||
const PropertyRNA *prop = (const PropertyRNA *)ptr->data;
|
||||
short version = 0;
|
||||
if (const DeprecatedRNA *deprecated = RNA_property_deprecated(prop)) {
|
||||
version = deprecated->removal_version;
|
||||
}
|
||||
|
||||
ARRAY_SET_ITEMS(value, version / 100, version % 100, 0);
|
||||
}
|
||||
|
||||
static bool rna_BoolProperty_default_get(PointerRNA *ptr)
|
||||
{
|
||||
PropertyRNA *prop = (PropertyRNA *)ptr->data;
|
||||
@@ -3338,6 +3382,32 @@ static void rna_def_property(BlenderRNA *brna)
|
||||
"Property is a path which supports the \"{variable_name}\" variable expression syntax, "
|
||||
"which substitutes the value of the referenced variable in place of the expression");
|
||||
|
||||
prop = RNA_def_property(srna, "is_deprecated", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_boolean_funcs(prop, "rna_Property_is_deprecated_get", nullptr);
|
||||
RNA_def_property_ui_text(prop, "Deprecated", "The property is deprecated");
|
||||
|
||||
prop = RNA_def_property(srna, "deprecated_note", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_string_funcs(
|
||||
prop, "rna_Property_deprecated_note_get", "rna_Property_deprecated_note_length", nullptr);
|
||||
RNA_def_property_ui_text(prop, "Deprecated Note", "A note regarding deprecation");
|
||||
|
||||
prop = RNA_def_property(srna, "deprecated_version", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
/* Use 3 values to match `bpy.app.version`. */
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_ui_text(prop, "Deprecated Version", "The Blender version this was deprecated");
|
||||
RNA_def_property_int_funcs(prop, "rna_Property_deprecated_version_get", nullptr, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "deprecated_removal_version", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Deprecated Removal Version", "The Blender version this is expected to be removed");
|
||||
RNA_def_property_int_funcs(
|
||||
prop, "rna_Property_deprecated_removal_version_get", nullptr, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "tags", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_enum_items(prop, dummy_prop_tags);
|
||||
|
||||
@@ -183,6 +183,20 @@ void pyrna_invalidate(BPy_DummyPointerRNA *self)
|
||||
self->ptr->invalidate();
|
||||
}
|
||||
|
||||
static void pyrna_prop_warn_deprecated(const PointerRNA *ptr,
|
||||
const PropertyRNA *prop,
|
||||
const DeprecatedRNA *deprecated)
|
||||
{
|
||||
PyErr_WarnFormat(PyExc_DeprecationWarning,
|
||||
1,
|
||||
"'%s.%s' is expected to be removed in Blender %d.%d",
|
||||
RNA_struct_identifier(ptr->type),
|
||||
RNA_property_identifier(prop),
|
||||
deprecated->removal_version / 100,
|
||||
deprecated->removal_version % 100,
|
||||
deprecated->note);
|
||||
}
|
||||
|
||||
#ifdef USE_PYRNA_INVALIDATE_GC
|
||||
# define FROM_GC(g) ((PyObject *)(((PyGC_Head *)g) + 1))
|
||||
|
||||
@@ -1436,6 +1450,10 @@ PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
|
||||
PyObject *ret;
|
||||
const int type = RNA_property_type(prop);
|
||||
|
||||
if (const DeprecatedRNA *deprecated = RNA_property_deprecated(prop)) {
|
||||
pyrna_prop_warn_deprecated(ptr, prop, deprecated);
|
||||
}
|
||||
|
||||
if (RNA_property_array_check(prop)) {
|
||||
return pyrna_py_from_array(ptr, prop);
|
||||
}
|
||||
@@ -1607,6 +1625,10 @@ static int pyrna_py_to_prop(
|
||||
/* XXX hard limits should be checked here. */
|
||||
const int type = RNA_property_type(prop);
|
||||
|
||||
if (const DeprecatedRNA *deprecated = RNA_property_deprecated(prop)) {
|
||||
pyrna_prop_warn_deprecated(ptr, prop, deprecated);
|
||||
}
|
||||
|
||||
if (RNA_property_array_check(prop)) {
|
||||
/* Done getting the length. */
|
||||
if (pyrna_py_to_array(ptr, prop, static_cast<char *>(data), value, error_prefix) == -1) {
|
||||
|
||||
Reference in New Issue
Block a user