Changeset View
Standalone View
source/blender/python/intern/bpy.c
| Show All 19 Lines | |||||
| * This file defines the '_bpy' module which is used by python's 'bpy' package | * This file defines the '_bpy' module which is used by python's 'bpy' package | ||||
| * to access C defined builtin functions. | * to access C defined builtin functions. | ||||
| * A script writer should never directly access this module. | * A script writer should never directly access this module. | ||||
| */ | */ | ||||
| #include <Python.h> | #include <Python.h> | ||||
| #include "BLI_string.h" | #include "BLI_string.h" | ||||
| #include "BLI_string_utils.h" | |||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BKE_appdir.h" | #include "BKE_appdir.h" | ||||
| #include "BKE_blender_version.h" | #include "BKE_blender_version.h" | ||||
| #include "BKE_bpath.h" | #include "BKE_bpath.h" | ||||
| #include "BKE_global.h" /* XXX, G_MAIN only */ | #include "BKE_global.h" /* XXX, G_MAIN only */ | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| ▲ Show 20 Lines • Show All 107 Lines • ▼ Show 20 Lines | static PyObject *bpy_blend_paths(PyObject *UNUSED(self), PyObject *args, PyObject *kw) | ||||
| list = PyList_New(0); | list = PyList_New(0); | ||||
| BKE_bpath_traverse_main(G_MAIN, bpy_blend_paths_visit_cb, flag, (void *)list); | BKE_bpath_traverse_main(G_MAIN, bpy_blend_paths_visit_cb, flag, (void *)list); | ||||
| return list; | return list; | ||||
| } | } | ||||
| PyDoc_STRVAR(bpy_flip_name_doc, | |||||
| ".. function:: flip_name(name)\n" | |||||
campbellbarton: Prefer name `strip_digits`, more closely follows Python's terminology (`str.isdigits()`… | |||||
| "\n" | |||||
| " Flip a name between left/right sides, useful for " | |||||
| " mirroring bone names.\n" | |||||
| "\n" | |||||
| " :arg name: bone name to flip\n" | |||||
| " :type name: string\n" | |||||
| " :return: The flipped name.\n" | |||||
Done Inline ActionsUse double tack for literals: ``.###`` campbellbarton: Use double tack for literals:
```
``.###``
``` | |||||
Done Inline ActionsCorrection: Use double back-tick for literals: campbellbarton: Correction: //Use double **back-tick** for literals:// | |||||
| " :rtype: string\n"); | |||||
| static PyObject *bpy_flip_name(PyObject *UNUSED(self), PyObject *value) | |||||
| { | |||||
| Py_ssize_t value_str_len; | |||||
| const char *value_str = PyUnicode_AsUTF8AndSize(value, &value_str_len); | |||||
| if (value_str == NULL) { | |||||
| PyErr_SetString(PyExc_TypeError, "expected a string"); | |||||
| return NULL; | |||||
Not Done Inline Actionsfrom_name can be positional only argument, an empty string can be used here "". campbellbarton: `from_name` can be positional only argument, an empty string can be used here `""`. | |||||
Done Inline ActionsPositional only arguments don't exist in Python, and even non-keyword-only arguments are usually discouraged, I don't see why we would want to prevent the caller from specifying the keyword, maybe I'm misunderstanding. Mets: Positional only arguments don't exist in Python, and even non-keyword-only arguments are… | |||||
| } | |||||
| /* Worst case we gain one extra byte (besides null-terminator) by changing | |||||
| "Left" to "Right", because only the first appearance of "Left" gets replaced. */ | |||||
| const size_t size = value_str_len + 2; | |||||
| char *name_flip_str = PyMem_MALLOC(size); | |||||
Not Done Inline ActionsNo need to call strlen, instead pass s# to _PyArg_ParseTupleAndKeywordsFast. campbellbarton: No need to call `strlen`, instead pass `s#` to `_PyArg_ParseTupleAndKeywordsFast`. | |||||
Done Inline ActionsCan you elaborate on this one? Would something like "s#|$O&:flip_name" feed the string length into an argument, or so? Mets: Can you elaborate on this one? Would something like `"s#|$O&:flip_name"` feed the string length… | |||||
Not Done Inline ActionsYes, check the documentation for usage. https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue campbellbarton: Yes, check the documentation for usage. https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue | |||||
Not Done Inline Actionscampbellbarton: Correct URL https://docs.python.org/3/c-api/arg.html#strings-and-buffers | |||||
Done Inline ActionsFollowing code crashes: Py_ssize_t from_name_len;
static const char *_keywords[] = {"from_name", "strip_digits", NULL};
static _PyArg_Parser _parser = {"s#|$O&:flip_name", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kw, &_parser, &from_name, &from_name_len, PyC_ParseBool, &strip_digits)) {
return NULL;
}The debugger is giving me something very odd: ==2372449==ERROR: AddressSanitizer: requested allocation size 0x7fff00000008 (0x7fff00001008 after adjustments for alignment, red zones etc.) exceeds maximum supported size of 0x10000000000 (thread T0)
#0 0x7ffff7682517 in malloc (/lib/x86_64-linux-gnu/libasan.so.6+0xb0517)
#1 0x1f2ea7f7 in _PyObject_Malloc Objects/obmalloc.c:1645
#2 0x1f2ea7f7 in _PyObject_Malloc Objects/obmalloc.c:1638Mets: Following code crashes:
```
Py_ssize_t from_name_len;
static const char *_keywords[] =… | |||||
| BLI_string_flip_side_name(name_flip_str, value_str, false, size); | |||||
| PyObject *return_value = PyUnicode_FromStringAndSize(name_flip_str, strlen(name_flip_str)); | |||||
| PyMem_FREE(name_flip_str); | |||||
Done Inline ActionsThis check isn't needed, _PyArg_ParseTupleAndKeywordsFast would return an error in this case, also the strlen above would have crashed if this is NULL. campbellbarton: This check isn't needed, `_PyArg_ParseTupleAndKeywordsFast` would return an error in this case… | |||||
| return return_value; | |||||
| } | |||||
| // PyDoc_STRVAR(bpy_user_resource_doc[] = /* now in bpy/utils.py */ | // PyDoc_STRVAR(bpy_user_resource_doc[] = /* now in bpy/utils.py */ | ||||
| static PyObject *bpy_user_resource(PyObject *UNUSED(self), PyObject *args, PyObject *kw) | static PyObject *bpy_user_resource(PyObject *UNUSED(self), PyObject *args, PyObject *kw) | ||||
| { | { | ||||
| const struct PyC_StringEnumItems type_items[] = { | const struct PyC_StringEnumItems type_items[] = { | ||||
| {BLENDER_USER_DATAFILES, "DATAFILES"}, | {BLENDER_USER_DATAFILES, "DATAFILES"}, | ||||
Not Done Inline ActionsNo need to use strlen here, BLI_string_flip_side_name now returns the string length. campbellbarton: No need to use `strlen` here, `BLI_string_flip_side_name` now returns the string length. | |||||
Done Inline ActionsWith this code: size_t size = from_name_len + 2; char *name_flip_str = PyMem_MALLOC(size); BLI_string_flip_side_name(name_flip_str, from_name, strip_number, size); PyObject *return_value = PyUnicode_FromStringAndSize(name_flip_str, size); I get strange results when I call the function from the PyAPI: >>> bpy.utils.flip_name('a.Right')
'a.Left\x00\x00p'
>>> bpy.utils.flip_name('a.Left')
'a.Right\x00'
>>> bpy.utils.flip_name('a.L')
Traceback (most recent call last):
File "<blender_console>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 4: invalid start byte
>>> bpy.utils.flip_name('a.R')
Traceback (most recent call last):
File "<blender_console>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 4: invalid start byteMets: With this code:
```
size_t size = from_name_len + 2;
char *name_flip_str = PyMem_MALLOC(size)… | |||||
Not Done Inline ActionsThe input size wont work, the return value is the length, e.g: const size_t name_flip_str_len = BLI_string_flip_side_name(name_flip_str, from_name, strip_digits, size); PyObject *return_value = PyUnicode_FromStringAndSize(name_flip_str, name_flip_str_len); PyMem_FREE(name_flip_str); return return_value; campbellbarton: The input size wont work, the return value is the length, e.g:
```
const size_t… | |||||
| {BLENDER_USER_CONFIG, "CONFIG"}, | {BLENDER_USER_CONFIG, "CONFIG"}, | ||||
| {BLENDER_USER_SCRIPTS, "SCRIPTS"}, | {BLENDER_USER_SCRIPTS, "SCRIPTS"}, | ||||
| {BLENDER_USER_AUTOSAVE, "AUTOSAVE"}, | {BLENDER_USER_AUTOSAVE, "AUTOSAVE"}, | ||||
| {0, NULL}, | {0, NULL}, | ||||
| }; | }; | ||||
| struct PyC_StringEnum type = {type_items}; | struct PyC_StringEnum type = {type_items}; | ||||
| const char *subdir = NULL; | const char *subdir = NULL; | ||||
| ▲ Show 20 Lines • Show All 168 Lines • ▼ Show 20 Lines | static PyMethodDef meth_bpy_script_paths = { | ||||
| bpy_script_paths_doc, | bpy_script_paths_doc, | ||||
| }; | }; | ||||
| static PyMethodDef meth_bpy_blend_paths = { | static PyMethodDef meth_bpy_blend_paths = { | ||||
| "blend_paths", | "blend_paths", | ||||
| (PyCFunction)bpy_blend_paths, | (PyCFunction)bpy_blend_paths, | ||||
| METH_VARARGS | METH_KEYWORDS, | METH_VARARGS | METH_KEYWORDS, | ||||
| bpy_blend_paths_doc, | bpy_blend_paths_doc, | ||||
| }; | }; | ||||
| static PyMethodDef meth_bpy_flip_name = { | |||||
| "flip_name", | |||||
| (PyCFunction)bpy_flip_name, | |||||
| METH_O, | |||||
| bpy_flip_name_doc, | |||||
| }; | |||||
| static PyMethodDef meth_bpy_user_resource = { | static PyMethodDef meth_bpy_user_resource = { | ||||
| "user_resource", | "user_resource", | ||||
| (PyCFunction)bpy_user_resource, | (PyCFunction)bpy_user_resource, | ||||
| METH_VARARGS | METH_KEYWORDS, | METH_VARARGS | METH_KEYWORDS, | ||||
| NULL, | NULL, | ||||
| }; | }; | ||||
| static PyMethodDef meth_bpy_system_resource = { | static PyMethodDef meth_bpy_system_resource = { | ||||
| "system_resource", | "system_resource", | ||||
| ▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | PyModule_AddObject(mod, | ||||
| meth_bpy_resource_path.ml_name, | meth_bpy_resource_path.ml_name, | ||||
| (PyObject *)PyCFunction_New(&meth_bpy_resource_path, NULL)); | (PyObject *)PyCFunction_New(&meth_bpy_resource_path, NULL)); | ||||
| PyModule_AddObject(mod, | PyModule_AddObject(mod, | ||||
| meth_bpy_escape_identifier.ml_name, | meth_bpy_escape_identifier.ml_name, | ||||
| (PyObject *)PyCFunction_New(&meth_bpy_escape_identifier, NULL)); | (PyObject *)PyCFunction_New(&meth_bpy_escape_identifier, NULL)); | ||||
| PyModule_AddObject(mod, | PyModule_AddObject(mod, | ||||
| meth_bpy_unescape_identifier.ml_name, | meth_bpy_unescape_identifier.ml_name, | ||||
| (PyObject *)PyCFunction_New(&meth_bpy_unescape_identifier, NULL)); | (PyObject *)PyCFunction_New(&meth_bpy_unescape_identifier, NULL)); | ||||
| PyModule_AddObject(mod, | |||||
| meth_bpy_flip_name.ml_name, | |||||
| (PyObject *)PyCFunction_New(&meth_bpy_flip_name, NULL)); | |||||
| /* register funcs (bpy_rna.c) */ | /* register funcs (bpy_rna.c) */ | ||||
| PyModule_AddObject(mod, | PyModule_AddObject(mod, | ||||
| meth_bpy_register_class.ml_name, | meth_bpy_register_class.ml_name, | ||||
| (PyObject *)PyCFunction_New(&meth_bpy_register_class, NULL)); | (PyObject *)PyCFunction_New(&meth_bpy_register_class, NULL)); | ||||
| PyModule_AddObject(mod, | PyModule_AddObject(mod, | ||||
| meth_bpy_unregister_class.ml_name, | meth_bpy_unregister_class.ml_name, | ||||
| (PyObject *)PyCFunction_New(&meth_bpy_unregister_class, NULL)); | (PyObject *)PyCFunction_New(&meth_bpy_unregister_class, NULL)); | ||||
| Show All 11 Lines | |||||
Prefer name strip_digits, more closely follows Python's terminology (str.isdigits(), string.digits, string.hexdigits).