Asset user count is (+1) in 5.0+

I’ve been working on a script that can automatically execute scripts attached to imported assets (while respecting and enforcing Blender’s script & security policy).
The goal is an add-on / extension that patches the drudgery and insecurity of Blender’s current import system when dealing with assets rigged with scripted powers.

I want it to work with Blender 3.0+. Most of my work has been using 4.5 (for nothing more than convenience) but I have been testing regularly against 3.0, 3.5, 5.0, and 5.2 as well.

There is a weirdness I need help understanding, though.

Attached TEXT data-blocks, when first imported, have two users in 5.0+, not one!
The following script can be used to see this yourself.

❶ Open Blender with the System Console available (not the Python Console in Blender)
❷ Paste and run the following script in the Text Editor:

import bpy
from bpy.app.handlers import persistent


@persistent
def import_handler(import_context):
	for item in import_context.import_items:
		if item.id_type in ('COLLECTION', 'ARMATURE', 'TEXT'):
			print("{:2d} {}".format(item.id.users, item.id.name_full))


def register():
	bpy.app.handlers.blend_import_post.append(import_handler)


if __name__ == "__main__":
	register()

❸ Open your Asset Browser and drop in any asset with an attached script (commonly found on character rigs — anything you’ve set up with Rigify will do).
I assume I am talking to the choir here, but just in case: If you do not have your Asset Browser set-up then any other import method will also do: File > Append, File > Link, drag & drop, etc.

❹ Check your System Console output. The collection and the armature (and most everything else that I’ve filtered out) will have a user count of 1.

But the rig_ui.py (or whatever text block attached to your data) will have:

  • (Blender < 5.0) user count == 1 (which is expected)
  • (Blender ≥ 5.0) user count == 2 (like, wut!?)

The result holds regardless of the Import Method.

❺ What is this extra user?
(Now in the Python Console in Blender) we find that in 5.0+ the data-block is used twice by the same object:

>>> bpy.data.user_map()[bpy.data.texts["rig_ui.py"]]
{bpy.data.objects['Armature'], bpy.data.objects['Armature']}

>>> |

Further exploration in the Outliner shows that the library links to a magic copy of the source blendfile in the asset library. Here I used the Flick model as an asset.

What is magic about it is that “Flick 2.1-asset.blend.001” does not exist anywhere I can find it, and I do not know why it exists at all.

Please explain this to me

I cannot find anything about it online, and I am really hoping I won’t have to try to chase this down in Blender’s source code.

Secondary question (for the tl;dr response)

Is it reasonable that I continue checking the number of users at import to see if this is the first import, only now just checking for users ≤ 2 when bpy.app.version ≥ (5,0,0)?

Or do I need to start handling blend_import_pre and cataloguing everything that exists prior to import so that I can perform a diff after?

This was a bug:

https://projects.blender.org/blender/blender/issues/151829

..but it may have be resurrected ??

Hmm, it looks related. The data-block being affected here is a TEXT, not an action, but as the bug exists I’ll have to code around it.

Do you think my solution is sufficiently correct (check user count depending on Blender version)?
EDIT: or check the user map for duplicates?

def is_single_user(item):
	count = item.users
	if count == 2: # https://projects.blender.org/blender/blender/issues/151829 ?
		return len(set(bpy.data.user_map()[item])) == 1
	return count == 1
	# For each imported data-block..
	for item in import_context.import_items:

		# If it is all of..
		if ((item.id_type == 'TEXT') and                # ..a text data-block
			bpy.data.texts[item.name].use_module and    # ..a registered script
			is_single_user(bpy.data.texts[item.name])): # ..the first time we’ve seen it

The “problem” with version checking for a bug might be that you have to know exactly for which exact version this does apply.. but also not including maybe futire fixes.. (so contantly checking about possible new versions.. or maybe even make this hack enable-able by the user in some advanced settings.. ??)

And maybe also if the file was done in that version and in which version one is working..

Another possibility might be if i understood thsi correctly:

These script might add some custom attribut to the asset so itself “knows” that the asset was imported for the first time and controlls its own count.. ??

Ah, sorry, I updated as you were answering.

I think I figured it. No version checking needed, just recognition that the error does occur and checking in the specific case that the user count is two.

Any thoughts on the <sourcefile>.001 stuff? I guess that isn’t related to my problem? Still curious tho…

Ohh.. if you can check for single user object and it still gives back 2 then this “simple” ..:grinning_cat_with_smiling_eyes:

The .001 is only in the example when duplicating and deleteing it.. of course when doing another import it also use these numbering..

If I understand you correctly:

check for single user object and it still gives back 2 then this

you mean that I should instance a single new object and see if it has a higher user count?

No. Never trust that any API will behave the same on two different objects without explicitly documented guarantees to that effect, even when in the same context, which is not possible here. I’ll verify with the user_map.


The .001 is only in the example when duplicating and deleting it..`

No duplication or deletion. I start each test with a fresh, empty file. Then drag & drop from the Asset Browser. The “duplicate file” does not appear visible to me anywhere but in the Data API of the Outliner. It doesn’t even appear in the duplicate user map (unless that second entry is actually pointing to the .001 file). I don’t think it actually exists — it is just an extraneous link in the source file chain, maybe?

Dang. Gonna have to read the source to figure it out.
I’m tired of reading the source.


I really do appreciate your help. Thank you for trying!

I was reading your code “wrong”.. :sweat_smile:

“We” might need a break :laughing: