JacquesLucke / blender_vscode

Visual Studio Code extension for Blender development.
MIT License
580 stars 75 forks source link

Weird "addon target directory" / "installed addons" behavior #178

Closed TomErnst closed 3 months ago

TomErnst commented 3 months ago

After a break of some months, I have changed my Blender version from 3.4 to 4.2, and something weird happened.

My old Blender addons still work fine with the hard link in the standard UserAppData-addonDirectory to my source directory E.g.: There is a hard link in “C:\Users\Tom\AppData\Roaming\Blender Foundation\Blender\4.2\scripts\addons\ossl” which hard-links to “c:\Blender\_addons\ossl”

But something didn’t work with a newly created addon, so I had a closer look and experienced the following: There were two installations in the Blender Preferences Window:

  1. One with the name as given in the addon’s "__init__.py" file: Hard link from “C:\Users\Tom\AppData\Roaming\Blender Foundation\Blender\4.2\scripts\addons\osx” to “c:\Blender\_addons\osx” as before
  2. One with the name as given in the addon’s "blender_manifest.toml" file Hard link from “C:\Users\Tom\AppData\Roaming\Blender Foundation\Blender\4.2\extensions\default_user\osx” to “c:\Blender\_addons\osx” This installation showed no preference properties.

I removed both hard links and deleted the "blender_manifest.tom" file, now everything works fine as with the old addons.

So I guess, the faults derived from this double installation produced in conjunction with the "blender_manifest.toml" file

Questions:

  1. Can the VSCode-addon be configured so that it doesn’t produce and use any longer the ..\scripts\addons\.. directory and instead only uses the ..\extensions\user_default\..? or 1a. Will it make any problems, if I add the "blender_manifest.toml" file only to installation packages, so that my addon works fine for the target users only in ..\extensions\user_default\..?
  2. Is there a better solution?
Mateusz-Grzelinski commented 3 months ago

if your addon contains file blender_manifest.toml it is considered by extension and linked to extensions\default_user, so what you experienced looks correct.

Do I understand correctly that your problem occured because there were 2 links to the same addon? if yes then you described similar issue to #176 - there is no solution now, the vs code does not clean after itself what causes problems. you will find some workaround for separating your work enviroments in 176.

TomErnst commented 3 months ago

Thanks so far, @Mateusz-Grzelinski, but it did not yet work as I want it to work. I could good live with the new position of the addon (as an extension) in ..\extensions\default_user\.. also during development ("separating it" is not necessary), but then THOSE preferences should be editable, and no second entry (from ..\scripts\addons\..) should exist. And I don't want to touch the development of my older addons (big infrastructure around ..\scripts\addons\..), so I want to avoid to deal with the environment variables. Meanwhile I removed _blinfo from my main "__init__.py" and restored the "blender_manifest.toml". Only one hard link has been created, this time in ..\extensions\default_user\... So far so good, but now he doesn't know my root namespace and is unable to activate the addon. What am I doing wrong?

Mateusz-Grzelinski commented 3 months ago

what do you mean by THOSE preferences? Blender preferences? VS code setting?

no second entry (from ..\scripts\addons..) should exist.

That is ideal, but currently VS code does not remove links that it creates so if you switch your code base between addon and extension you will end uo with duplicate entries.

Meanwhile I removed bl_info from my main "init.py" and restored the "blender_manifest.toml".

That is meaningless from the point of view of blender_vscode. I am not sure how blender interpretes when you support both addon and extension.

So far so good, but now he doesn't know my root namespace and is unable to activate the addon.

What is root namespace in this context? Are you aware the extension has different addon name?

>>> import bl_ext
                 .blender_org
                 .system
                 .user_default
>>> import bl_ext.user_default.<extension-id>
TomErnst commented 3 months ago

Sorry, I am German, this may be a source of misunderstandings.

what do you mean by THOSE preferences? Blender preferences? VS code setting?

If I write "preferences" I always mention the blender addon preferences, which are reachable in Blender by Menu "Edit"-"Preferences", Tab "Add-ons" (btw, if I look into Tab "Get Extensions", I see the "TipiGenerator" as well as "Installed"). Otherwise I would have written "settings".

That is ideal, but currently VS code does not remove links that it creates so if you switch your code base between addon and extension you will end uo with duplicate entries.

I don't switch between addon and extension. I would be fine with my code being treated (by blender_vscode and Blender) as an extension now (Blender 4.2 and in future), can't see any difference anyway, except concerning the parent directory.

_Meanwhile I removed blinfo from my main **"__init_\.py" and restored the "blendermanifest.toml"**.

That is meaningless from the point of view of blender_vscode. I am not sure how blender interpretes when you support both addon and extension.

As I said: I don't make a difference, I don't switch, but obviously one hardlink (..\scripts\addons\..) is created, when I have bl_info, and the other hardlink is created, when I have a "blender_manifest.toml".

What is root namespace in this context? Are you aware the extension has different addon name?

Isn't the root namespace taken from the code directory name, equal if it resides in ..\scripts\addons\.. or ..\extensions\user_defaults\.. ?? The directory's name is "TipiGenerator" in both cases and should be the root namespace.

> import bl_ext.blender_org
> import bl_ext.system
> import bl_ext.user_default
> import bl_ext.user_default.TipiGenerator
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\Tom\AppData\Roaming\Blender Foundation\Blender\4.2\extensions\user_default\TipiGenerator\__init__.py", line 23, in <module>
    import TipiGenerator.util as util
ModuleNotFoundError: No module named 'TipiGenerator'

TipiGenerator.zip

Mateusz-Grzelinski commented 3 months ago

Thanks for the code, that clarifies it.

What you experienced is kind of a breaking change in extension system or a bad practise together that worked together to break your code.

Long story short: use relative import.

Previously (legacy adddon) was added to sys.path so using using absolute imports worked fine:

>>> import TipiGenerator

In new extension system your extension is added in different path and all your package is now expected to be self contained:

>>> import bl_ext.user_default.TipiGenerator

So if you want to keep using your absolute import you would need to use the full path in every single import in code what is really not a great idea:

try: 
    import TipiGenerator.util as util  # works in addon
except ModuleNotFoundError:
    import bl_ext.user_default.TipiGenerator.util as util  # works in extension

That is why use relative import.

This is your imports fixed: TipiGenerator.zip

Mateusz-Grzelinski commented 3 months ago

look at the imports in this addon: https://github.com/b-init/ImagePaste/blob/main/__init__.py I like to use it as example, it is just complext enough.

TomErnst commented 3 months ago

Many thanks for your clarification … yes, that explains the "weird" behavior which I experienced. Btw, "self contained" I would call the old ("addon") technique, not the the new ("extension") one, because having the root namespace at the root makes the "addon" architectually a capsule, where the "extension" is architecturally a part of the surrounding "bl_ext"-subsystem. In fact, the explanation is a (luckily only small and handleable) catastrophe for me, because I am used to build big object models (originally I am a top level .NET software architect) with many files, the object model structure expressing itself also in the subdirectory/namespace structure. Of course it can be handled the way you proposed, but it is in fact bad architecture to rely on thrown exceptions at each file loading, and it makes the code less overviewable on opening of files (significantly larger header, if there are many imports). I have not tried yet, but I hope, at least

try: import TipiGenerator
except: import bl_ext.user_default.TipiGenerator as TipiGenerator

will also work (I am cross-referencing many classes by starting with the root), but that can't be used for "from" imports :( At least it is not practicable for me to use relative import across e.g. 5-6 levels up. Especially class inheritances often need that. And imagine the consequences, if I move a whole class tree (e.g. 3 levels of average 3-4 subdirectories per level) to another place, e.g. one level deeper sweating and swearing And imagine, what if there come additional/ other directories than "user_default" into the game. And I will have to adapt my "utils.imports"/"utils.release", where I also always start with the root namespace. Sadly imports are always in-code, can't replaced by function calls, at least not for intellisense. This is where the fact that Python does not support macros comes back to torment us. In C# I have always the choice to use own namespaces (≈"addon") or use existing namespaces (≈"extension"). In fact, I call the new architecture a very bad decision of the Blender developers … there is NO reason to embed "extensions" instead of treating them as "plugins" as hitherto. My current project is to generate whole native villages (mainly procedurally/automated, also structures editable in Blender, generic, much use of dynamic classes built from xml-files) for roleplay and educational purposes in OpenSim, including all assets … imagine the many object classes in a highly complex structure/ object model tree … … Aaaaargh … I think, I will stay with the old "addon" model for now, maybe make a file converter program for later deployment later on.

But nevertheless many thanks again, for your efforts, time and sustainable explanation.

Greetings from old Tom ("Owl Eyes" in all virtual worlds and nickname in RealLife since my youth 🙂)

Mateusz-Grzelinski commented 3 months ago

And I will have to adapt my "utils.imports"/"utils.release", where I also always start with the root namespace.

remember that there is also a lazy way. I think there are programs that automatically change imports to relative image

there is NO reason to embed "extensions" instead of treating them as "plugins" as hitherto

Extensions can be downloaded from internet. Would you allow to add arbitrary import into your global namespace? I dont know about that...

Currently sys.path is not touched when using extensions: https://projects.blender.org/blender/blender/issues/125107 and migration guide: https://docs.blender.org/manual/en/4.2/extensions/addons.html#legacy-vs-extension-add-ons

Instead of Try and except block for import you can use something sensible like addon_utils.check_extension.

An you know how it goes, you can always cheat and modify sys.path anyways https://github.com/JacquesLucke/blender_vscode/blob/master/pythonFiles/launch.py#L8

And it is python, so you can cheat even more!!! No need for macros to create some crazy code.

import sys

sys.modules["TipiGenerator"] = sys

import TipiGenerator

TipiGenerator.exit(1)

Uptill now I managed to work just fine with python modules/packages, the namespacing issues never really bugged me.

Mateusz-Grzelinski commented 3 months ago

You can always make your case on https://blender.chat/ python api channel :) I had a big break from blender for a year of 2 so I completely missed the extension discussion.

Glad I helped.