How to set the value of a MeshPolygonStringProperty to a string?

I am trying to set a string attribute per polygon on a mesh. To create the property, I use:

a = m.polygon_layers_string.new(name='object')
x = a.data[0]

but when I try to set the data:

x.value = 'blah'
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
TypeError: bpy_struct: item.attr = val: MeshPolygonStringProperty.value expected a bytes type, not str

When I inspect the types:

type(x)
<class 'bpy.types.MeshPolygonStringProperty'>

and

type(x.value)
<class 'bytes'>

But the documentation here mentions the type of x.value should be a string, or am I misreading?

What is the correct way to store a string per polygon?

ps.
Sorry for the double post with stack exchange, I only found this site after.

Try this: x.value = "blah".encode()

Thanks for the answer, I had tried that, and while it does not fail, I struggle to get anything back but:

str(names.data[0].value)
"b'\\x00'"

I have been unable to get anything but that value from the layer.

names.data[0].value.decode()

I’ve only ever done this in bmesh, maybe try the same.

Thanks again for trying to help, it is much appreciated.

I tried that as well earlier, but I just have no luck with this:

m=bpy.data.meshes["my_mesh"]
layer =m.polygon_layers_string.new(name='my_string')
layer.data[0].value='hello'.encode()
print(layer.data[0].value.decode())

print(layer.data[0].value)
b'\x00'

I was avoiding bmeshes as they are missing some features I need (loop normals mostly) so I end up jumping back and forth anyway, but this might be a good reason.

I worked around this by just splitting the meshes by name, it’s not as nice as having one mesh with object names ( in our pipeline), but it works. No need for the names anymore.

Cheers,
Koen

@koen5
You seem on the right track. See this example from eight years ago.
Multiple Labels on Polygons (blenderartists.org, November 2012)

However, I think you have hit on a bug. When I run a fairly bleeding edge version of blender in debug mode [2.92.0 Alpha rB194a57fd631f 2020-11-14 21:54 (-4:00 EST)] I get an assertion abort when trying to read out whatever value I may have set in the value property. From the python console editor:

>>> aod = bpy.context.active_object.data
>>> aod.polygon_layers_string.new(name="Fun Strings! One for each polygon in the mesh!")
bpy.data.meshes['Cube'].polygon_string_layers["Fun Strings! One for each polygon in the mesh!"]

>>> # Lets give the sixth polygon in the mesh a fun name.
>>> aod.polygon_layers_string["Fun Strings! One for each polygon in the mesh!"].data[6].value = b"Ian Hubert"
>>> # So far, so good. but when I hit carriage return on the next line, I'll blow up Blender... Let us attempt to dump to the console the 'Ian Hubert' polygon name
>>> aod.polygon_layers_string["Fun Strings! One for each polygon in the mesh!"].data[6].value

which leads to an abort message in the shell I launched blender in…

...(omitted stack frame...)
BLI_assert failed: source/blender/makesrna/intern/rna_access.c:3354,   RNA_property_string_get_alloc(), at 'buf[length] == '\0''
Aborted

If you don’t run a debug version of blender, the assignment silently fails, and you see what you have been seeing.
I believe your code will work on an earlier version of Blender, but I’m not sure how early - 2.83 LTS? - depends on how long this bug has been lurking. This is probably the making of a bug report that I’ll post in the sweet by-and-by. But in the meantime, let me assure you that - as far as I can see - you are not losing your mind. At least on the account of this issue…

Ha, thanks very much for this reply. I am just getting my feet wet in the blender API, so it’s good to hear it’s just a bug and not my complete misunderstanding of the data structure :slight_smile: I have a good workaround for now, but will definitely get back to this later.

I am loosing my mind anyway these days, having blender to blame for it was kind of re-assuring, now I need to go back to blaming myself…

Cheers,
koen

This is probably the making of a bug report that I’ll post in the sweet by-and-by.

An existing (and long-standing) TO-DO task already covers this issue: Blender development todo list – Scripting (T55367), which is a roll-up of a number of incomplete and outstanding items needed for Python scripting. This particular issue falls under the Python/RNA API Design TODO rubric. The resolution of Task 32581 Mesh properties API does not allow for zeros in byte array is a partial fix that does not fully address the to-do; a mesh polygon boolean/float/integer/byte buffer layer that a python script writer might use to associate additional properties with mesh polygons is not wholly in place yet. There are more TODO’s then there are developers to do them, a perennial Blender problem.

The immediate trigger to the abort that I observed seems to be in void rna_MeshStringProperty_s_set(), introduced in the commit to - at least partially - address T32581; it creates and populates a simple structure, MStringProperty which ultimately is associated with a specific mesh polygon in the property layer. It has two parts: a fixed buffer (call it s) and a count of bytes (call it len). In the example above, ‘Ian Hubert’ are the data and ‘10’ is the count. rna_MeshStringProperty_s_set() sets the fixed buffer part, but does not set the len part, defaulting it to zero. That has ramifications for client callers of this function, which are lead to believe that these MStringProperty buffers, established by this function, have zero bytes of data, the genesis of the abort I described in the last post.

I gather that this deficiency is a known issue, awaiting the good, full, sweetness of time to fix. I also gather that - names like MeshStringProperty notwithstanding - this portion of the Python API is being retooled to accommodate buffers of bytes, and byte buffers are not strings. They may have zero-valued bytes in their midst, which can confound naive uses of tools like strlen().

How rna_MeshStringProperty_s_set() goes about setting buffer length will take some thought and design choices. Perhaps callers should just tell this function the length of their byte buffers. Perhaps not. These are API protocol decisions that haven’t been made yet.

You can read the date stamps on T32581 and the commit that addressed it - partially - as well as I can. Everyone - alas! - is dancing as fast as they can. So, if you have workarounds, they may just have to work around for some time to come. Take care.