Python API, changes to type registration in 2.8 #52599

Closed
opened 2017-08-31 08:48:07 +02:00 by Campbell Barton · 17 comments

Recently D2774 was committed to master which replaces list lookups with a hash.

This exposed naming collisions in add-ons, at first I thought it reasonable to rename classes to account for this however considering how many 3rd party addons there are, it's not practical.

This raises the question of why we have a global name-space for registered classes at all (currently in bpy.types) at all.

This tasks outlines changes to registration that avoid problems with add-on naming collisions, and keeps class registration manageable.

Note: none of these changes apply to 2.7x releases.

General Proposal


  • Only built-in (non STRUCT_RUNTIME) types are added to bpy.types.
Scripts that need to access registered classes can:
  • For known types - access them from the module that defines them (as you would in any other kind of class in Python).

  • For introspection/scanning for all types use bpy.types.*.__subclasses__() (needed for generating docs for eg).

  • No checks for naming collisions are performed but...

  • Each type has a unique identifier that can collide. (for example, two operators can't have the same bl_idname, two RenderEngine's ... UIList's etc)

  • On collision with dynamic types with matching ID's. The new types will overwrite the old ones.

    While it might be good to change this to prevent accidents, it's been working for years, it's handy for Python developers who want to re-run their scripts to try modified behavior, we could make this more strict so scripts don't accidentally clobber eachother's ID's, this would be better to handle separately.

Option 1) Exception for Compatibility (now in master)

From searching over add-ons I only found only one case of an add-on referencing it's own class via bpy.types
so thats easily resolved.

The issue is there are classes that are accessed to extend Blender, mainly menu's but also panels and headers.

We could update scripts to replace bpy.types.INFO_MT_file_export with bl_ui.space_info.INFO_MT_file_export,
but this is going to break a lot of scripts and exposes Blender's internal module layout.

Currently register-able types are:

  • AddonPreferences
  • Header
  • KeyingSetInfo
  • Menu
  • NodeSocket
  • NodeSocketInterface
  • NodeTree
  • Operator
  • Panel
  • PropertyGroup
  • RenderEngine
  • UIList

I'm proposing [Menu, Panel, Header, UIList, Operator] continue to be accessible from bpy.types under their bl_idname (not Python class-name).
so scripts can use them to manipulate the interface.

We'll have to ensure bl_idname conventions are followed *_MT_*, *_PT_*, *_HT_*, *_UL_*, *_OT_*.
(this is going to break some scripts, but means using a global name-space won't collide with different types).

Option 2) Expose general types via bpy.types.*.find(...)

Instead of keeping some classes in bpy.types, we could use an function. eg:

bpy.types.INFO_MT_file_export would be accessed as bpy.types.Menu.find("INFO_MT_file_export")

This has the advantage that we don't need to be strict about naming, it also simplifies the code (not having to track public/private structs).

The main disadvantage is scripts will need to be updated, however it will be quite straightforward and nearly all scripts will need some updates for 2.8x anyway.


Update: submitted patch: D2816 (option 1)

Recently [D2774](https://archive.blender.org/developer/D2774) was committed to master which replaces list lookups with a hash. This exposed naming collisions in add-ons, at first I thought it reasonable to rename classes to account for this however considering how many 3rd party addons there are, it's not practical. This raises the question of why we have a global name-space for registered classes at all (currently in `bpy.types`) at all. This tasks outlines changes to registration that avoid problems with add-on naming collisions, and keeps class registration manageable. Note: none of these changes apply to 2.7x releases. General Proposal **** - Only built-in (non `STRUCT_RUNTIME`) types are added to `bpy.types`. ``` Scripts that need to access registered classes can: ``` - For known types - access them from the module that defines them *(as you would in any other kind of class in Python).* - For introspection/scanning for all types use `bpy.types.*.__subclasses__()` *(needed for generating docs for eg).* - No checks for naming collisions are performed **but...** - Each type has a unique identifier that can collide. *(for example, two operators can't have the same `bl_idname`, two RenderEngine's ... UIList's etc)* - On collision with dynamic types with matching ID's. The new types will overwrite the old ones. *While it might be good to change this to prevent accidents, it's been working for years, it's handy for Python developers who want to re-run their scripts to try modified behavior, we could make this more strict so scripts don't accidentally clobber eachother's ID's, this would be better to handle separately.* Option 1) Exception for Compatibility *(now in master)* ---- From searching over add-ons I only found only one case of an add-on referencing it's own class via `bpy.types` so thats easily resolved. The issue is there are classes that are accessed to extend Blender, mainly menu's but also panels and headers. We *could* update scripts to replace `bpy.types.INFO_MT_file_export` with `bl_ui.space_info.INFO_MT_file_export`, but this is going to break a lot of scripts and exposes Blender's internal module layout. Currently register-able types are: - AddonPreferences - Header - KeyingSetInfo - Menu - NodeSocket - NodeSocketInterface - NodeTree - Operator - Panel - PropertyGroup - RenderEngine - UIList I'm proposing `[Menu, Panel, Header, UIList, Operator]` continue to be accessible from `bpy.types` under their `bl_idname` (not Python class-name). so scripts can use them to manipulate the interface. We'll have to ensure `bl_idname` conventions are followed `*_MT_*`, `*_PT_*`, `*_HT_*`, `*_UL_*`, `*_OT_*`. *(this is going to break some scripts, but means using a global name-space won't collide with different types).* Option 2) Expose general types via `bpy.types.*.find(...)` ------ Instead of keeping some classes in `bpy.types`, we could use an function. eg: `bpy.types.INFO_MT_file_export` would be accessed as `bpy.types.Menu.find("INFO_MT_file_export")` This has the advantage that we don't need to be strict about naming, it also simplifies the code (not having to track public/private structs). The main disadvantage is scripts will need to be updated, however it will be quite straightforward and nearly all scripts will need some updates for 2.8x anyway. ---- Update: submitted patch: [D2816](https://archive.blender.org/developer/D2816) (option 1)
Bastien Montagne was assigned by Campbell Barton 2017-08-31 08:48:07 +02:00
Author
Owner

Changed status to: 'Open'

Changed status to: 'Open'
Author
Owner

Added subscribers: @ideasman42, @mont29, @Sergey

Added subscribers: @ideasman42, @mont29, @Sergey
Campbell Barton changed title from Python API, changes to type registration to Python API, changes to type registration in 2.8 2017-08-31 08:50:26 +02:00
Member

Added subscriber: @JacquesLucke

Added subscriber: @JacquesLucke
Member

Here are a few ideas that I have on the topic:

For every register-able type Blender could store a hash table (bl_idname -> cls).
So whenever you call bpy.utils.register_class(MyClass), the type is inserted into the corresponding hash table.

Registered types can than be accessed like this: bpy.types.TYPE.get(bl_idname)
This would eliminate the need for strange bl_idname conventions which I personally do not like at all.
The only naming rule that should be enforced (imo) is that bl_idnames should be valid Python identifiers. This also makes the system more future proof if we decide to use a different access pattern later on.
I'm not sure if it is good that Operators are handled in a special way (the bl_idname needs exactly one . somewhere in the middle, while on both sides are valid Python identifiers) but I guess that can't really be changed easily.
We could also have bpy.types.TYPE.get_all() to get all registered subclasses of a type. The problem with TYPE.__subclasses__() is that there can be subclasses that are not registered.

Just wanted to share a few different ideas on the reloading issue as well although I'm not fully convinced by them.

  • Print a warning in the console whenever a type is overwritten (this is quite essential I guess)
  • Make replacing an existing type an exception if Blender is not in a "developer mode". This allows the users of addons which don't have this enabled to report name-collisions early. (They might have addons installed the original developer does not have)
  • Change the signature of the registration function to register_class(cls, group = ""). This group string (which does not have to be set by the developer) is stored in the hash table next to the class. The hash table would then look like this: bl_idname -> (cls, group). Whenever a new type is registered, the group is checked. If it is equal, cls is replaced, otherwise an exception is raised. Types that are not part of a group (group = "") cannot be reloaded at all. The group should usually be the addon name, but can also be just a random string. It only has to stay constant within one execution of Blender.

(btw: Also Node is register-able)

Here are a few ideas that I have on the topic: For every register-able type Blender could store a hash table (`bl_idname -> cls`). So whenever you call `bpy.utils.register_class(MyClass)`, the type is inserted into the corresponding hash table. Registered types can than be accessed like this: `bpy.types.TYPE.get(bl_idname)` This would eliminate the need for strange `bl_idname` conventions which I personally do not like at all. The only naming rule that should be enforced (imo) is that `bl_idname`s should be valid Python identifiers. This also makes the system more future proof if we decide to use a different access pattern later on. I'm not sure if it is good that `Operator`s are handled in a special way (the `bl_idname` needs exactly one `.` somewhere in the middle, while on both sides are valid Python identifiers) but I guess that can't really be changed easily. We could also have `bpy.types.TYPE.get_all()` to get all registered subclasses of a type. The problem with `TYPE.__subclasses__()` is that there can be subclasses that are not registered. Just wanted to share a few different ideas on the reloading issue as well although I'm not fully convinced by them. - Print a warning in the console whenever a type is overwritten (this is quite essential I guess) - Make replacing an existing type an exception if Blender is not in a "developer mode". This allows the users of addons which don't have this enabled to report name-collisions early. (They might have addons installed the original developer does not have) - Change the signature of the registration function to `register_class(cls, group = "")`. This group string (which does not have to be set by the developer) is stored in the hash table next to the class. The hash table would then look like this: `bl_idname -> (cls, group)`. Whenever a new type is registered, the group is checked. If it is equal, `cls` is replaced, otherwise an exception is raised. Types that are not part of a group (`group = ""`) cannot be reloaded at all. The `group` should usually be the addon name, but can also be just a random string. It only has to stay constant within one execution of Blender. (btw: Also `Node` is register-able)

Added subscriber: @satishgoda1

Added subscriber: @satishgoda1
Author
Owner

In #52599#456054, @JacquesLucke wrote:
Here are a few ideas that I have on the topic:

For every register-able type Blender could store a hash table (bl_idname -> cls).
So whenever you call bpy.utils.register_class(MyClass), the type is inserted into the corresponding hash table.

We have per type hash maps for almost all registerable types already (except the new PropertyGroup).

While writing the proposal I considered supporting:

  mt = bpy.types.Menu.find("SOME_MT_menu")

The main reason against this is it would break a lot of scripts, (anything that touches the file-menu - so import/exporters).

For 2.8x perhaps this is OK, I'd rather avoid large breakages for minimal gains.

Registered types can than be accessed like this: bpy.types.TYPE.get(bl_idname)
This would eliminate the need for strange bl_idname conventions which I personally do not like at all.
The only naming rule that should be enforced (imo) is that bl_idnames should be valid Python identifiers. This also makes the system more future proof if we decide to use a different access pattern later on.
I'm not sure if it is good that Operators are handled in a special way (the bl_idname needs exactly one . somewhere in the middle, while on both sides are valid Python identifiers) but I guess that can't really be changed easily.

We need operators names the way they are so they can be accessed as bpy.ops.xxx.xxx().

We could also have bpy.types.TYPE.get_all() to get all registered subclasses of a type. The problem with TYPE.__subclasses__() is that there can be subclasses that are not registered.

Scripts can check the subclass is registered by calling cls.is_registered().

Just wanted to share a few different ideas on the reloading issue as well although I'm not fully convinced by them.

  • Print a warning in the console whenever a type is overwritten (this is quite essential I guess)
  • Make replacing an existing type an exception if Blender is not in a "developer mode". This allows the users of addons which don't have this enabled to report name-collisions early. (They might have addons installed the original developer does not have)
  • Change the signature of the registration function to register_class(cls, group = ""). This group string (which does not have to be set by the developer) is stored in the hash table next to the class. The hash table would then look like this: bl_idname -> (cls, group). Whenever a new type is registered, the group is checked. If it is equal, cls is replaced, otherwise an exception is raised. Types that are not part of a group (group = "") cannot be reloaded at all. The group should usually be the addon name, but can also be just a random string. It only has to stay constant within one execution of Blender.

(btw: Also Node is register-able)

Not sure about each type needing a group, in general though Im not against something like what you're suggesting. I just rather not make that change part of this proposal.

> In #52599#456054, @JacquesLucke wrote: > Here are a few ideas that I have on the topic: > > For every register-able type Blender could store a hash table (`bl_idname -> cls`). > So whenever you call `bpy.utils.register_class(MyClass)`, the type is inserted into the corresponding hash table. We have per type hash maps for almost all registerable types already (except the new PropertyGroup). While writing the proposal I considered supporting: ``` mt = bpy.types.Menu.find("SOME_MT_menu") ``` The main reason against this is it would break a lot of scripts, (anything that touches the file-menu - so import/exporters). For 2.8x perhaps this is OK, I'd rather avoid large breakages for minimal gains. > Registered types can than be accessed like this: `bpy.types.TYPE.get(bl_idname)` > This would eliminate the need for strange `bl_idname` conventions which I personally do not like at all. > The only naming rule that should be enforced (imo) is that `bl_idname`s should be valid Python identifiers. This also makes the system more future proof if we decide to use a different access pattern later on. > I'm not sure if it is good that `Operator`s are handled in a special way (the `bl_idname` needs exactly one `.` somewhere in the middle, while on both sides are valid Python identifiers) but I guess that can't really be changed easily. We need operators names the way they are so they can be accessed as `bpy.ops.xxx.xxx()`. > We could also have `bpy.types.TYPE.get_all()` to get all registered subclasses of a type. The problem with `TYPE.__subclasses__()` is that there can be subclasses that are not registered. Scripts can check the subclass is registered by calling `cls.is_registered()`. > Just wanted to share a few different ideas on the reloading issue as well although I'm not fully convinced by them. > > - Print a warning in the console whenever a type is overwritten (this is quite essential I guess) > - Make replacing an existing type an exception if Blender is not in a "developer mode". This allows the users of addons which don't have this enabled to report name-collisions early. (They might have addons installed the original developer does not have) > - Change the signature of the registration function to `register_class(cls, group = "")`. This group string (which does not have to be set by the developer) is stored in the hash table next to the class. The hash table would then look like this: `bl_idname -> (cls, group)`. Whenever a new type is registered, the group is checked. If it is equal, `cls` is replaced, otherwise an exception is raised. Types that are not part of a group (`group = ""`) cannot be reloaded at all. The `group` should usually be the addon name, but can also be just a random string. It only has to stay constant within one execution of Blender. > > (btw: Also `Node` is register-able) Not sure about each type needing a group, in general though Im not against something like what you're suggesting. I just rather not make that change part of this proposal.
Member

While writing the proposal I considered supporting:

mt = bpy.types.Menu.find("SOME_MT_menu")
The main reason against this is it would break a lot of scripts, (anything that touches the file-menu - so import/exporters).

For 2.8x perhaps this is OK, I'd rather avoid large breakages for minimal gains.

The benefit of using bpy.types.Menu.find(NAME) over bl_ui.space_info.NAME is that the first already contains the type information. That means that NAME does not have to contain any type information anymore. In my opinion this is highly preferable (works well with having one bl_idname namespace per type instead of a global namespace).
I think updating existing scripts can mostly be automated here as well. By using bl_ui.space_info.NAME NAME stil has to be unique across multiple types. Maybe I also understood that wrong, I didn't know about the bl_ui module before.

Scripts can check the subclass is registered by calling cls.is_registered().

Ah good to know. I haven't found that in the api docs.

Not sure about each type needing a group, in general though Im not against something like what you're suggesting. I just rather not make that change part of this proposal.

Alright, that can be discussed later.

My main incentive here is that I don't want to use these __MT__, ... conventions. :D

> While writing the proposal I considered supporting: > > ```mt = bpy.types.Menu.find("SOME_MT_menu")``` > The main reason against this is it would break a lot of scripts, (anything that touches the file-menu - so import/exporters). > > For 2.8x perhaps this is OK, I'd rather avoid large breakages for minimal gains. The benefit of using `bpy.types.Menu.find(NAME)` over `bl_ui.space_info.NAME` is that the first already contains the type information. That means that `NAME` does not have to contain any type information anymore. In my opinion this is highly preferable (works well with having one `bl_idname` namespace per type instead of a global namespace). I think updating existing scripts can mostly be automated here as well. By using `bl_ui.space_info.NAME` `NAME` stil has to be unique across multiple types. Maybe I also understood that wrong, I didn't know about the `bl_ui` module before. > Scripts can check the subclass is registered by calling `cls.is_registered()`. Ah good to know. I haven't found that in the api docs. > Not sure about each type needing a group, in general though Im not against something like what you're suggesting. I just rather not make that change part of this proposal. Alright, that can be discussed later. My main incentive here is that I don't want to use these `__MT__`, ... conventions. :D
Author
Owner

@JacquesLucke, While bl_ui is a regular Python module it's currently not documented as part of the API, I'd rather not expose it publicly.

Added the option for an accessor function as Option 2 in the proposal.

@JacquesLucke, While `bl_ui` is a regular Python module it's currently not documented as part of the API, I'd rather not expose it publicly. Added the option for an accessor function as `Option 2` in the proposal.

I’d go for option 1 (“I'd rather avoid large breakages for minimal gains.” -> 100% agree!).

And strict (naming) conventions are actually a big +1 from my side, if we had had them in the first place we would not have hit those collisions issues at all. It’s pretty stupid to write two public classes with same name, at best it's confusing, and unless you have a real strict and good control over your scopes it will bite you at one point or the other. Python coders tend to be sloppy, see quality of code in many existing addons, losing conventions will only makes things worse.

@ideasman42 scripts like i18n messages extraction etc. are already using bpy.types.*.__subclasses__() trick anyway, at least due to stupid operators (iirc, their definition generates two classes with same name, one for operator, one for its properties, and in bpy.types.foo you only have access to the propertygroup one, or something like that).

I’d go for option 1 (“*I'd rather avoid large breakages for minimal gains.*” -> 100% agree!). And strict (naming) conventions are actually a big +1 from my side, if we had had them in the first place we would not have hit those collisions issues at all. It’s pretty stupid to write two public classes with same name, at best it's confusing, and unless you have a real strict and good control over your scopes it will bite you at one point or the other. Python coders tend to be sloppy, see quality of code in many existing addons, losing conventions will only makes things worse. @ideasman42 scripts like i18n messages extraction etc. are already using `bpy.types.*.__subclasses__()` trick anyway, at least due to stupid operators (iirc, their definition generates two classes with same name, one for operator, one for its properties, and in `bpy.types.foo` you only have access to the propertygroup one, or something like that).

Added subscriber: @VukGardasevic

Added subscriber: @VukGardasevic

I'm supporting conventions in naming. bl_idname and the registered class name should follow the same pattern, i.e. the bl_idname and the class name can be guessed correctly from each other. Plus the class name should reflect the internal structure one.

Currently while editing the an add-on, in the class overview, there can be different naming conventions inside the same script. This makes comprehension, debugging difficult, not to mention accessing them from search.

Example from contrib (one add-on that had a naming collision)
example_classes.jpg
In that example class RemuevePropiedades is an Operator, View3DMCPanel is a stub class for inheritance.

The problem is as something is not an clear simple rule that is mandatory, people don't think it is important and they spent very little time thinking about the naming conventions. The result is a mess (for bl_idname some add-ons use context like object.some_operator, some use an name of add-on or an abbreviation of it like terrain.some_operator , opr.some_operator , sky.dyn or something remotely related to the functionality like add.some_operator).

Still, everything depends on extensiveness of other API changes. If most of the add-ons end up breaking ( for instance accessing selection, layers are changed, add-ons relying on Blender Internal etc.) then it makes sense to impose naming rules during the refactor/rewrite of them. If the changes only affect a smaller number of add-ons, then some other solutions are also legitimate (allowing similar naming as today as an exemption to the rule for third party add-ons).

Also +1 for strict naming conventions. :)

I'm supporting conventions in naming. `bl_idname` and the registered class name should follow the same pattern, i.e. the bl_idname and the class name can be guessed correctly from each other. Plus the class name should reflect the internal structure one. Currently while editing the an add-on, in the class overview, there can be different naming conventions inside the same script. This makes comprehension, debugging difficult, not to mention accessing them from search. Example from contrib (one add-on that had a naming collision) ![example_classes.jpg](https://archive.blender.org/developer/F750149/example_classes.jpg) In that example class `RemuevePropiedades` is an Operator, `View3DMCPanel` is a stub class for inheritance. The problem is as something is not an clear simple rule that is mandatory, people don't think it is important and they spent very little time thinking about the naming conventions. The result is a mess (for bl_idname some add-ons use context like `object.some_operator`, some use an name of add-on or an abbreviation of it like `terrain.some_operator `, `opr.some_operator` , `sky.dyn` or something remotely related to the functionality like `add.some_operator`). Still, everything depends on extensiveness of other API changes. If most of the add-ons end up breaking ( for instance accessing selection, layers are changed, add-ons relying on Blender Internal etc.) then it makes sense to impose naming rules during the refactor/rewrite of them. If the changes only affect a smaller number of add-ons, then some other solutions are also legitimate (allowing similar naming as today as an exemption to the rule for third party add-ons). Also +1 for strict naming conventions. :)
Member

I'm not against naming conventions. However I'm against knowledge duplication in code. Eg: when I already say that a specific class is a Panel, then I don't need to put that information into the identifier.

If you want that the identifier and class name can be guessed from each other, there just should not be both but only one string in the first place.

I agree that the operator categories are a mess in many addons. I think there are multiple reasons for that:

  • There is no clear definition of what kind of operator go into which category. So you would have to go to the manual and see all the categories that exist, then you go inside and see that it is also a mess.
  • For low level operators it might be easy to find a specific catogory. However, once you create larger macros that touch many areas of Blender, it is nearly impossible to select only one category to put the operator in.
  • People, at least me, fear that they run into collisions with other addons. It is not very unlikely that this happens when everyone puts their operators into the correct categories. So you start to create your own category -> your own namespace, to feel safer. Many large addons (Animation Nodes, Retopoflow, and possibly more) take this approach as it is pretty much impossible to make sure that no one else uses one of your operator names. However it is much easier to check that no one uses the same operator category that you are using.

I'm not saying that the current operator naming "rules" are bad but that they don't work well for larger addons. I'm not sure yet what the best way is to solve this.

It’s pretty stupid to write two public classes with same name

True, if you are the only person who writes addons. As an addon developer you don't have control over other addon developers.

We could update scripts to replace bpy.types.INFO_MT_file_export with bl_ui.space_info.INFO_MT_file_export,
but this is going to break a lot of scripts and exposes Blender's internal module layout.

While bl_ui is a regular Python module it's currently not documented as part of the API, I'd rather not expose it publicly.

Addons also need access to that data, so the two statements don't work well together. (or not?)

Speaking from an addon developer perspective (I don't know anything about the internals):
Blender is responsible for making sure that addon developers can feel save and that naming collisions with other addons are impossible. And by that I mean that it should not be enforced by convention but by data structure.

This would be the ideal goal we should strive for. None of the proposals above archieve this goal yet. I don't know if it is possible to archieve this goal for Blender 2.8, if not, we should try to get close IMO.

I'm not against naming conventions. However I'm against knowledge duplication in code. Eg: when I already say that a specific class is a Panel, then I don't need to put that information into the identifier. If you want that the identifier and class name can be guessed from each other, there just should not be both but only one string in the first place. I agree that the operator categories are a mess in many addons. I think there are multiple reasons for that: - There is no clear definition of what kind of operator go into which category. So you would have to go to the manual and see all the categories that exist, then you go inside and see that it is also a mess. - For low level operators it might be easy to find a specific catogory. However, once you create larger macros that touch many areas of Blender, it is nearly impossible to select only one category to put the operator in. - People, at least me, fear that they run into collisions with other addons. It is not very unlikely that this happens when everyone puts their operators into the correct categories. So you start to create your own category -> your own namespace, to feel safer. Many large addons (Animation Nodes, Retopoflow, and possibly more) take this approach as it is pretty much impossible to make sure that no one else uses one of your operator names. However it is much easier to check that no one uses the same operator category that you are using. I'm not saying that the current operator naming "rules" are bad but that they don't work well for larger addons. I'm not sure yet what the best way is to solve this. > It’s pretty stupid to write two public classes with same name True, if you are the only person who writes addons. As an addon developer you don't have control over other addon developers. > We could update scripts to replace `bpy.types.INFO_MT_file_export` with `bl_ui.space_info.INFO_MT_file_export`, > but this is going to break a lot of scripts and exposes Blender's internal module layout. > While `bl_ui` is a regular Python module it's currently not documented as part of the API, I'd rather not expose it publicly. Addons also need access to that data, so the two statements don't work well together. (or not?) Speaking from an addon developer perspective (I don't know anything about the internals): *Blender **is responsible** for making sure that addon developers can feel save and that naming collisions with other addons are impossible. And by that I mean that it should not be enforced by convention but by data structure.* This would be the ideal goal we should strive for. None of the proposals above archieve this goal yet. I don't know if it is possible to archieve this goal for Blender 2.8, if not, we should try to get close IMO.
Author
Owner

Changed status from 'Open' to: 'Resolved'

Changed status from 'Open' to: 'Resolved'
Author
Owner

Committed (Option 1) 636baa598a & 0bbae3f3f6

Committed (Option 1) 636baa598a & 0bbae3f3f6
Author
Owner

In #52599#456103, @JacquesLucke wrote:
I'm not against naming conventions. However I'm against knowledge duplication in code. Eg: when I already say that a specific class is a Panel, then I don't need to put that information into the identifier.

Currently you do, because it's accessed via bpy.types, moving elsewhere could work but we opted for Option 1.

If you want that the identifier and class name can be guessed from each other, there just should not be both but only one string in the first place.

This is the case, Python developers don't need to define an bl_idname in this case the class name will be used.

I agree that the operator categories are a mess in many addons. I think there are multiple reasons for that:

  • There is no clear definition of what kind of operator go into which category. So you would have to go to the manual and see all the categories that exist, then you go inside and see that it is also a mess.
  • For low level operators it might be easy to find a specific catogory. However, once you create larger macros that touch many areas of Blender, it is nearly impossible to select only one category to put the operator in.
  • People, at least me, fear that they run into collisions with other addons. It is not very unlikely that this happens when everyone puts their operators into the correct categories. So you start to create your own category -> your own namespace, to feel safer. Many large addons (Animation Nodes, Retopoflow, and possibly more) take this approach as it is pretty much impossible to make sure that no one else uses one of your operator names. However it is much easier to check that no one uses the same operator category that you are using.

I'm not saying that the current operator naming "rules" are bad but that they don't work well for larger addons. I'm not sure yet what the best way is to solve this.

We could have a convention that all addons use ADDON_ID_##_own_name,
effectively a (category, name) tuple.

It’s pretty stupid to write two public classes with same name

True, if you are the only person who writes addons. As an addon developer you don't have control over other addon developers.

Right, so a convention would be helpful.

We could update scripts to replace bpy.types.INFO_MT_file_export with bl_ui.space_info.INFO_MT_file_export,
but this is going to break a lot of scripts and exposes Blender's internal module layout.

While bl_ui is a regular Python module it's currently not documented as part of the API, I'd rather not expose it publicly.

Addons also need access to that data, so the two statements don't work well together. (or not?)

Not sure what you mean. Addon have access to it via bpy.types.

Speaking from an addon developer perspective (I don't know anything about the internals):
Blender is responsible for making sure that addon developers can feel save and that naming collisions with other addons are impossible. And by that I mean that it should not be enforced by convention but by data structure.

There are many areas in computing where avoiding collisions is done by conventions - commands in your $PATH, Python module names, key bindings between your OS and applications... etc.

I see what you're saying, but blender is not just a python library, all the logic has C internals and is defined in C. This means in some cases Python API follows C conventions.

While its possible to have an extra level of categories - this is a big change - then every reference to an operator would need to be "category.subcategory.toolname" or so.

This would be the ideal goal we should strive for. None of the proposals above archieve this goal yet. I don't know if it is possible to archieve this goal for Blender 2.8, if not, we should try to get close IMO.

At the moment I think its not very practical, and we can manage the problem using conventions.

> In #52599#456103, @JacquesLucke wrote: > I'm not against naming conventions. However I'm against knowledge duplication in code. Eg: when I already say that a specific class is a Panel, then I don't need to put that information into the identifier. Currently you do, because it's accessed via `bpy.types`, moving elsewhere could work but we opted for *Option 1*. > If you want that the identifier and class name can be guessed from each other, there just should not be both but only one string in the first place. This is the case, Python developers don't need to define an `bl_idname` in this case the class name will be used. > I agree that the operator categories are a mess in many addons. I think there are multiple reasons for that: > - There is no clear definition of what kind of operator go into which category. So you would have to go to the manual and see all the categories that exist, then you go inside and see that it is also a mess. > - For low level operators it might be easy to find a specific catogory. However, once you create larger macros that touch many areas of Blender, it is nearly impossible to select only one category to put the operator in. > - People, at least me, fear that they run into collisions with other addons. It is not very unlikely that this happens when everyone puts their operators into the correct categories. So you start to create your own category -> your own namespace, to feel safer. Many large addons (Animation Nodes, Retopoflow, and possibly more) take this approach as it is pretty much impossible to make sure that no one else uses one of your operator names. However it is much easier to check that no one uses the same operator category that you are using. > > I'm not saying that the current operator naming "rules" are bad but that they don't work well for larger addons. I'm not sure yet what the best way is to solve this. We could have a convention that all addons use `ADDON_ID_##_own_name`, effectively a (category, name) tuple. >> It’s pretty stupid to write two public classes with same name > > True, if you are the only person who writes addons. As an addon developer you don't have control over other addon developers. Right, so a convention would be helpful. > >> We could update scripts to replace `bpy.types.INFO_MT_file_export` with `bl_ui.space_info.INFO_MT_file_export`, >> but this is going to break a lot of scripts and exposes Blender's internal module layout. > >> While `bl_ui` is a regular Python module it's currently not documented as part of the API, I'd rather not expose it publicly. > > Addons also need access to that data, so the two statements don't work well together. (or not?) Not sure what you mean. Addon have access to it via `bpy.types`. > > Speaking from an addon developer perspective (I don't know anything about the internals): > *Blender **is responsible** for making sure that addon developers can feel save and that naming collisions with other addons are impossible. And by that I mean that it should not be enforced by convention but by data structure.* There are many areas in computing where avoiding collisions is done by conventions - commands in your $PATH, Python module names, key bindings between your OS and applications... etc. I see what you're saying, but blender is not just a python library, all the logic has C internals and is defined in C. This means in some cases Python API follows C conventions. While its possible to have an extra level of categories - this is a big change - then every reference to an operator would need to be "category.subcategory.toolname" or so. > This would be the ideal goal we should strive for. None of the proposals above archieve this goal yet. I don't know if it is possible to archieve this goal for Blender 2.8, if not, we should try to get close IMO. At the moment I think its not very practical, and we can manage the problem using conventions.
Member

I was wondering, how are the types selected that require _MT_, ... in the bl_idname? Because atm this convention only exists for some types, right? Most of the types you mention in your initial post don't require this (yet), correct?

It’s pretty stupid to write two public classes with same name

True, if you are the only person who writes addons. As an addon developer you don't have control over other addon developers.

Right, so a convention would be helpful.

I'm not sure how a convention helps here. It might even increase the likelyhood that two developers name different operators the same.

I was wondering, how are the types selected that require `_MT_`, ... in the `bl_idname`? Because atm this convention only exists for some types, right? Most of the types you mention in your initial post don't require this (yet), correct? >>> It’s pretty stupid to write two public classes with same name >>True, if you are the only person who writes addons. As an addon developer you don't have control over other addon developers. >Right, so a convention would be helpful. I'm not sure how a convention helps here. It might even increase the likelyhood that two developers name different operators the same.

If the add-on module name is included in the name not so much since it has to be unique for it to be activated properly (without the duplicates warning).
Also on the other side, having a prefix like that, it'll make search and replace much easier. :)
Having to think only about an unique name of the module to be used inside the prefix is less of a possible surface area for collisions.

Of course bl_idname complicates things a bit.

Let's say the the prefix is :
ADDON_ID_MATERIALS_UTILS_add_material what the bl_idname would be? Maybe materials_utils.add_material ?

  • In cases like this the module name is used as the first word which should be unique. However with context of the operator will not be clear when accessing from search.
  • The module/script name can be long.
  • Still there could be a name collision.

Some of the concerns can be addressed by developer tool / add-on where it can be easily enabled by the user and do a one button check for collisions.
That could be part of the package manager - like an option for checking for conflicts > output to a file like system info.

If the add-on module name is included in the name not so much since it has to be unique for it to be activated properly (without the duplicates warning). Also on the other side, having a prefix like that, it'll make search and replace much easier. :) Having to think only about an unique name of the module to be used inside the prefix is less of a possible surface area for collisions. Of course bl_idname complicates things a bit. Let's say the the prefix is : `ADDON_ID_MATERIALS_UTILS_add_material` what the bl_idname would be? Maybe `materials_utils.add_material `? - In cases like this the module name is used as the first word which should be unique. However with context of the operator will not be clear when accessing from search. - The module/script name can be long. - Still there could be a name collision. Some of the concerns can be addressed by developer tool / add-on where it can be easily enabled by the user and do a one button check for collisions. That could be part of the package manager - like an option for checking for conflicts > output to a file like system info.
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
5 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#52599
No description provided.