From 98752a1f3025a138b5a2f70f60a2cca1e89d2f66 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 5 Apr 2013 00:30:32 +0000 Subject: [PATCH] py api additions needed for fixing [#34864]. - add rna property 'as_bytes' method so you can get a string property as python bytes (bypass encoding). - make bpy.path.abspath/relpath compatible with bytes. - add 'relpath' option to bpy_extras.image_utils.load_image(), so you can load an image relative to a path. --- release/scripts/modules/bpy/path.py | 41 ++++++++++++++----- .../scripts/modules/bpy_extras/image_utils.py | 9 ++++ source/blender/python/intern/bpy_rna.c | 35 ++++++++++++++++ 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py index 6c91568cbc1..cfc0f474215 100644 --- a/release/scripts/modules/bpy/path.py +++ b/release/scripts/modules/bpy/path.py @@ -48,6 +48,10 @@ from _bpy_path import (extensions_audio, ) +def _getattr_bytes(var, attr): + return var.path_resolve(attr, False).as_bytes() + + def abspath(path, start=None, library=None): """ Returns the absolute path relative to the current blend file @@ -60,13 +64,22 @@ def abspath(path, start=None, library=None): convenience, when the library is not None its path replaces *start*. :type library: :class:`bpy.types.Library` """ - if path.startswith("//"): - if library: - start = _os.path.dirname(abspath(library.filepath)) - return _os.path.join(_os.path.dirname(_bpy.data.filepath) - if start is None else start, - path[2:], - ) + if isinstance(path, bytes): + if path.startswith(b"//"): + if library: + start = _os.path.dirname(abspath(_getattr_bytes(library, "filepath"))) + return _os.path.join(_os.path.dirname(_getattr_bytes(_bpy.data, "filepath")) + if start is None else start, + path[2:], + ) + else: + if path.startswith("//"): + if library: + start = _os.path.dirname(abspath(library.filepath)) + return _os.path.join(_os.path.dirname(_bpy.data.filepath) + if start is None else start, + path[2:], + ) return path @@ -79,10 +92,16 @@ def relpath(path, start=None): when not set the current filename is used. :type start: string """ - if not path.startswith("//"): - if start is None: - start = _os.path.dirname(_bpy.data.filepath) - return "//" + _os.path.relpath(path, start) + if isinstance(path, bytes): + if not path.startswith(b"//"): + if start is None: + start = _os.path.dirname(_getattr_bytes(_bpy.data, "filepath")) + return b"//" + _os.path.relpath(path, start) + else: + if not path.startswith("//"): + if start is None: + start = _os.path.dirname(_bpy.data.filepath) + return "//" + _os.path.relpath(path, start) return path diff --git a/release/scripts/modules/bpy_extras/image_utils.py b/release/scripts/modules/bpy_extras/image_utils.py index 36994d3fddd..5c63ce1218e 100644 --- a/release/scripts/modules/bpy_extras/image_utils.py +++ b/release/scripts/modules/bpy_extras/image_utils.py @@ -31,6 +31,7 @@ def load_image(imagepath, ncase_cmp=True, convert_callback=None, verbose=False, + relpath=None, ): """ Return an image from the file path with options to search multiple paths @@ -57,6 +58,8 @@ def load_image(imagepath, convert it to a PNG and return the PNG's path. For formats blender can read, simply return the path that is given. :type convert_callback: function + :arg relpath: If not None, make the file relative to this path. + :type relpath: None or string :return: an image or None :rtype: :class:`bpy.types.Image` """ @@ -100,6 +103,12 @@ def load_image(imagepath, if place_holder and image is None: image = _image_load_placeholder(path) + if image: + if relpath is not None: + # make relative + from bpy.path import relpath as relpath_fn + image.filepath_raw = relpath_fn(path, start=relpath) + return image # ------------------------------------------------------------------------- diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index d6a82ce43ea..df66ef92316 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -3324,6 +3324,40 @@ static PyObject *pyrna_prop_path_from_id(BPy_PropertyRNA *self) return ret; } +PyDoc_STRVAR(pyrna_prop_as_bytes_doc, +".. method:: as_bytes()\n" +"\n" +" Returns this string property as a byte rather then a python string.\n" +"\n" +" :return: The string as bytes.\n" +" :rtype: bytes\n" +); +static PyObject *pyrna_prop_as_bytes(BPy_PropertyRNA *self) +{ + + if (RNA_property_type(self->prop) != PROP_STRING) { + PyErr_Format(PyExc_TypeError, + "%.200s.%.200s.as_bytes() must be a string", + RNA_struct_identifier(self->ptr.type), RNA_property_identifier(self->prop)); + return NULL; + } + else { + PyObject *ret; + char buf_fixed[256], *buf; + int buf_len; + + buf = RNA_property_string_get_alloc(&self->ptr, self->prop, buf_fixed, sizeof(buf_fixed), &buf_len); + + ret = PyBytes_FromStringAndSize(buf, buf_len); + + if (buf_fixed != buf) { + MEM_freeN(buf); + } + + return ret; + } +} + PyDoc_STRVAR(pyrna_struct_type_recast_doc, ".. method:: type_recast()\n" "\n" @@ -4685,6 +4719,7 @@ static struct PyMethodDef pyrna_struct_methods[] = { static struct PyMethodDef pyrna_prop_methods[] = { {"path_from_id", (PyCFunction)pyrna_prop_path_from_id, METH_NOARGS, pyrna_prop_path_from_id_doc}, + {"as_bytes", (PyCFunction)pyrna_prop_as_bytes, METH_NOARGS, pyrna_prop_as_bytes_doc}, {"__dir__", (PyCFunction)pyrna_prop_dir, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} };