「Dev talk:Source/BPython/API/bpy api」の版間の差分

提供: wiki
移動先: 案内検索
 
(1版 をインポートしました)
 
(相違点なし)

2018年6月29日 (金) 02:51時点における最新版

Note
Please sign your name to your comments people. And if you start a new section, sign your name there too, please. - joeedh


Blender API

Controversial API decisions

See the current BPY API Docs here

bpy auto import?

Should the bpy API be loaded so the user doesn't have to be explicitly imported. At the moment "import bpy" is not required.

Meeting results:

  • no auto import
  • create menu item & templates for blank scripts

For Automatically Importing

  • one less line for users to have to type each time.
  • "bpy" is a small enough name that scripts can type it in without the need for doing something like from Blender import *
  • having bpy available as a name all scripts use makes it easier to move between editing different scripts
    Currently with the Blender module, you can't always copy and paste between scripts because scripts import Blender differently.
    This is also a small block in the way of people collaborating on scripts since some people do import Blender or from Blender import * or import Blender as B or from Blender import Mesh, Window
Perhaps auto importing may be non-standard when you're talking from a standalone shell script point of view, but in the context of built-in scripting languages for 3D and other graphics apps, it's quite standard. As far as I can determine, neither Mel, nor Maxscript, nor Lscript, nor Cinema 4d's COFFEE require any kind of boilerplate importing for native modules, and XSI's Python API [1] seems to only require explicit importing when you're using extra modules outside the usual set. Other graphics applications that use scripting extensively such as Flash and Director don't require any kind of importing either. FWIW I think it might be useful in such API re-thinks for Bpy developers to look at how things are done in other apps as reference. --Matt Ebb
Try and convince kaito with the argument "That's how other people do it"!
I'm not trying to convince kaito, I'm rebutting the "it's non-standard" argument and trying to convince the bpy custodians ;). Saying we shouldn't do something just because other people do it is even more silly than saying we should do it merely because other people do it. It's not much of a logical stretch to believe that a) makers of commercial 3d applications want to make things that their users will appreciate and b) the desires of their users and of Blender users may well be similar. All the same, Ton definitely values researching how things are done in other applications, in order to make informed choices. Many of the tools he's worked on in Orange and out of it have been strongly influenced by tools in other applications (as a concrete example, walk cycle modifiers, were very much inspired by XSI's motion deform [2]). --Matt Ebb
The collaboration argument: Collaboration is about communication. Using import bpy puts things in the bpy. namespace. Using 'import bpy as foo puts everything in foo. . In either case, a quick search and replace makes names compatible between scripts. --Stivs 18:37, 24 March 2007 (CET)
People's different use of modules makes doing a search/replace impractical in most cases, since they will import some submodules, and even if you figure out what needs renaming, it's nicer not to have to --Ideasman42 22:50, 24 March 2007 (CET)

Against Automatically Importing

  • Few python based scripting systems do this already (it's nonstandard)
  • It is not Pythonic

Other than the language builtins, you *always* import the specific modules you need in a python program. Why doesn't Python simply auto-import the 40 or 50 most common modules? Efficiency and bloat are first two reasons that come to mind.

Rebuttal - We're talking about importing exactly *one* module here, and it's the one most like to be imported in a *Blender python* script. --User:khpylon 20:35, 24 March 2007 (PDT)

A programmer should be able to control where and how modules appear in the namespace. Namespaces exist for a reason - to prevent name collisions. Example: from os import *; from Blender import *; - now which *.sys module is 'sys'?

Users may want the flexibility to do an alternative import such as import bpy as B

Rebuttal - If scripters really want to they can do B = bpy; del bpy so the flexibility is not completely lost --Ideasman42 22:55, 24 March 2007 (CET)

  • Legacy issues

Once we automagically import a module it will always be there. Always. This alone is sufficient argument against the idea. Look at the trouble we have with name collisions trying to tidy up the API now.

As a principle, "easy things should be easy, complicated things should not be difficult". Auto-importing bpy makes one easy thing easier, but unnecessarily complicates everything else.

I realize that for the naive scripter, these arguments are falling on deaf ears. For them the idea of saving a line of typing has great appeal. However, we give up quite a bit for that small convenience. Whether the argument is efficiency, namespace pollution, or the legacy burden, this idea is ++ungood. --Stivs 08:28, 24 March 2007 (CET)

Rebuttal - "complicates everything else"? I admit I have no strong feeling for or against auto-inclusion -- hell, "import bpy" vs "import Blender" will already save me four keystrokes per script -- but this conjecture seems extreme. --User:khpylon 20:35, 24 March 2007 (PDT)

Explicit autoimport - Best of both worlds?

I agree that normal scripting should not require boilerplate. But I also wish the embedding would not be done so that clarity with non-Blender Python world would be sacrificed, meaning that from looking at a script it would not be possible to straightforwardly tell the dependencies.

What about having the bpy module automagically there for textblocks with the extension .bpy? The UI could activate some nice BPY icon for those, and when doing 'new script' from menu it would do that automatically too. Then if a larger bpy system uses also some general py modules, that do not depend on Blender, they could be just added to a .blend retaining the .py extension, and they'd have the standard py icon. This is not a huge point but I just find that just clarity would be good.

A downside to that is that normal py registered associations for .py files would not work, e.g. if someone wants to edit a .bpy file in an external editor it would not be automatically handled as py. Then again, as .py are often executable (if you doubleclick on one in a window manager) in the operating system, but .bpy:s are not, so perhaps having different file associations would be good? Perhaps we could even associate .bpy to run Blender with that script?

If .bpy is not a working solution, are there others that would give us the best of both worlds? Or do we just go for having bpy always there when a py is run inside Blender? --Toni Alatalo

Rebuttal This unnecessarily linking the filename to how scripts work, is itself introducing a new level of confusion. What if somebody renames the script, imports it and it won't work because blender concatenates the name? Also, any file handling by text editorsm web browsers, OS mime types that see a .py file as an ASCII text editable file will be lost. It also is not obvious, a user copying another's code could wonder why theirs doesn't work when it does in other script. --Ideasman42 23:31, 24 March 2007 (CET)

Constructors VS dataseq.new()

Meeting Results:

  • dataseq.new()is good
  • also need constructors
  • location of constructors in the namespace is undecided
  • c-tors will use a scene argument. default is current scene


At the moment we are using dataseq.new(). There are many args for and against.

Examples are

scn = bpy.scenes.new('myscene')
me = bpy.meshes.new('mymesh')
ob = scn.objects.new(me)
img = bpy.images.load('//myimage.jpg')

VS

scn = bpy.scene('myscene')
me = bpy.mesh('mymesh')
ob = bpy.object(me, active=scn)
img = bpy.image('mymage', '//myimage.jpg')

# or if class instances can exist outside of blenders data
scn = bpy.scene('myscene')
bpy.scenes.append(scn)
me = bpy.mesh('mymesh')
bpy.meshes.append(me)
ob = bpy.objects(me)
scn.objects.append(ob)
img = bpy.image('mymage', '//myimage.jpg')

For Constructors

  • This is the standard way object oriented python works. Not only experienced programmers, but people who have done simple scripts have learned to use them - e.g. to open a file you do f = file('/tmp/x.txt'), not files.new() (but yes, if you then stop your script the file is closed and resource freed)
  • Short and straightforward to write: me = bpy.Mesh()
  • Would not obfuscate what sequences are - keeping the separations of a collection, a class/type, and an instance clear. They really are different things, for different purposes - overloading a single thing to mean them all perhaps means less variable names to remember, but does it create confusion about what things are? And to use an object the user has to look up the class documentation anyway, and understand what it refers to (unless Campbell manages to fuse the attributes and methods of the different Blender types to these sequences too :)
    • Rebuttal The point of extending Python sequences to do more things is so we can take advantage of Python's ability to create custom types. You could say that we are bastardizing the sequence.
      Or cleaning the API up by making a PyType that encapsulates the needs of Blender to manage data.
      Only allowing...
      me = bpy.meshes['foo']
      for me in bpy.meshes
      seems to add many variables to bpy.* without them being useful enough to warrant their inclusion at such a root level.
      At that point we would need (for every type).
      bpy.meshes
      bpy.mesh() # constructor
      bpy.Mesh.Types... # depending on how the constants are added
      This gives you an API that has many slightly different names used for different things that would get confusing to work with. It may be better to accept these types are not just simple sequences and use these custom PyTypes to deal with as many operations relating to those types as possible.

--

  • I don't see why the Classes have to be a part of the bpy module. We are assuming it *has* to be bpy.mesh or bpy.Mesh as the class instance. Why not put the Classes in another namespace? This could be something like 'classes'. e.g.
from classes import *
m = Mesh()

Ascotan 21:54, 23 March 2007 (CET)

Rebuttal It seems to me that this is moving the new API away from the goal of less special cases that the user to has to know about.

# To make a new object just do
from classes import *
me = Mesh()

This may work OK once the user reads some examples etc, but its not obvious at first, or learnable.

  • What does that mean? "moving the new API away from the goal of less special cases" So class instantiation is a 'special case'? And i assume "bpy.mesh.new()" will be obvious to a python programmer wihtou looking at the documentation? Ascotan 17:46, 26 March 2007 (CEST)

--

  • One of the biggest reasons to use constructors over factory functions is that you can use the class instance to type check a variable:
m = Mesh()
if isinstance(m, Mesh):
   print True

Without having access to the class instance (i.e. classes generated by factory functions) you can't properly type check a variable. Ascotan 21:56, 23 March 2007 (CET)

Rebuttal isinstance(m, Mesh) is nice, but not a reason to remove bpy.meshes.new(), we could 1) have bpy.classes.mesh and me.meshes.new or 2) have bpy.types.mesh like with the current API. --Ideasman42 03:52, 25 March 2007 (CEST)

Against Constructors

  • Even though the correct object-oriented way to create data, Python is not a strictly OO language and there is no reason to follow the OO data model where it doesn;t make sense.
    • Rebuttal What on earth does this mean? Do you mean that not all has to be in a class, like in Java? I don't know what that has to do with this. Everything in Python is an object, and they are typically created by calling the constructor (or using the literal like "" or []. Sure you can also make any function to create an object, like sequence.new()s now do, but that is true of everything-in-classes like langs like Java too (any method can return an instance of anything) --antont
    • More Rebuttal This is simply incorrect. Python is an OO language. All Python basic types are first class objects that can be used as base classes for creating new types. Historically, there was a distinction between used-defined types and classes but this has been disappearing since around version 2.2
That user-defined types can be base classes is one argument for exposing the constructors for our BPy types - so they can be used as base classes, an often requested feature that was not possible before.--stivs
  • using constructors does not make it obvious that new data is being added to Blender's database.
    In most cases where constructors are used, the object is then added by the scripter to the database.
    • Rebuttal a counter rationale to this is that as bpy is embedded in Blender, py there works like Blender normally. And with thin wrapping it is a rule that if you create a Blender object, it is created inside Blender, added to the Blender list etc. So normal py syntax could do what the normal Blender thing is.
      • Re-Rebuttal The but this is how blender does it doesn't make sense. Blender can't make a mesh all by itself, not linked to an object or scene, so we're not dealing with the same situation.
  • The API can't implement constructors in a way that works as a python developer expects.
    Example
    me = bpy.mesh()
    del me
    - should remove the mesh because the data should be garbage collected by Python where as in Blender the mesh will still exist.
    • Rebuttal then again del on a single object is rarely used for anything? And if something, for managing memory (hurrying garbage collection up?). del x never removes anything from a container (e.g. if a reference to the same ob is in a list, it stays), so no one would expect it to remove data from the Blender DB. (del list[i] is another thing and brings up the question: should we implement del scene.objects["name"] ?)
      • Re-Rebuttal agree if me is added to a list in the first place. Since me its not. del me should remove it, as with any Python class.
  • using constructors adds variable names that are only used for creation, more variables for the user to remember.
  • Constructors are not used in many other (newer) parts of the API. doing so changes the direction the Python API has been going. Examples
    • metaball.elements.add()
    • mesh.verts.extend()
    • modifiers.add()

Constants?

At the moment bpy.* has no access to constants.

As an example of how constants can be accessed currently.

Blender.Mesh.Modes.TWOSIDED

How could this be added?

bpy.constants.MESH_MODE_TWOSIDED # (1
or
bpy.meshes.MODE_TWOSIDED # (2
or...
me.MODE_TWOSIDED # (3
or...
bpy.Mesh.TWOSIDED # (4


Meeting Results:

  • no decision at the moment
  • Class.const is a strong favorite.
  • bpy.dataseq.const seems to be popular


Option 1

Having a global pool of constants could be useful.

Option 2

Having constants attached to the data sequence is non standard but it has the advantage that we dont need to add a section for constants anywhere else, less variable names for the scripter to remember is good too.


Rebuttal Apart from just being nonstandard, having the constant in the sequence is confusing - the code looks like the constant was somehow specific to that instance of the sequence, like a mode it is having. The common ways of having constants in classes or modules does not have this problem: there it is clear that the constants are not specific to a certain instance (like a given sequence at a time), but invariable static things.

Re-Rebuttal I disagree with the instance of sequence part of the argument, bpy.meshes is only ever created once and even if the user assigns it to a variable they are both the same instance and access the same mesh data.

Option 3

Accessing the constant from the data itself. Currently Blender's lamp module does this has the advantage that the constants you need are available without referring to nested variables in other parts of the API.

The disadvantage of this is you can't access a constant if you don't have data available... though probably not so common, if you even were in a situation where a constant needed to be accessed and you had to make some data just to get it, that would be very frustrating.

Option 4

A place to have constants is with the type they belong to. E.g.

bpy.Mesh.TWOSIDED

That is of course dependant on the larger question, whether bpy will have the Blender types/classes in its scope. In the current/old API that would be:

Blender.Mesh.Mesh.TWOSIDED #although you could argue for having it in the module too

Types/classes are available independently of whether you have data (an instance) available or not.

bpy.dataseq.load() or just use .new()

At the moment we have

snd = bpy.sounds.load('filename')
img = bpy.images.load('filename')

It has been suggested that we use the following syntax

bpy.sounds.new(filename = 'sound.wav')

to load images and sounds.

Meeting results:

  • .new( filename='foo.xx')
  • .new( 'name', 'foo.xx') - correction? I dont think we agree'd to use keywords, in this case its probably less confusing not to, 1 arg is new, 2 args will load, though that means the user cant use the filename as the name if it has a path --Ideasman42 05:21, 26 March 2007 (CEST)

For Using .new() Only

  • Less functions associated with bpy.dataseq
  • Only sound and image use load() so having it is not needed with most other data types.

Against Using .new() Only

  • Function names should say that they do rather then overload a single function with options to make it act differently.
    bpy.images.load() does what it says.
  • Applications have a New and Open menu items, The functionality is profoundly different.
  • A new image can already be created img = bpy.images.new('name', width, height) so having one new work as expected and other load a new image from a file, is adding 2 different command into one function.

Intergrating Other Modules

At the moment bpy has support for all datatypes (bpy.meshes, bpy.scenes etc) as well as bpy.libraries and bpy.config

But how should we go about adding less data centric modules like Blender.Window, Draw, BLG, sys etc.

--

  • These modules are not "Class" modules therfore they will continue to exist in their current form.

Ascotan 22:11, 23 March 2007 (CET)

  • Rebuttal what does it matter if they are "Class" modules or not? we still want to have a way to access these functions from bpy if bpy is eventually to replace the need for 'import Blender' --Ideasman42 23:06, 24 March 2007 (CET)

Documentation

Deprecation

To favor adoption of the new API, we can either:

  1. Document the old ways as not recommended / deprecated and eventually remove them from the API and its docs, as usual.
  2. Recreate the API Ref Guide this time: remove from it any reference to parts that should not be used anymore. This is not meant as a general guideline for the future, but only to be done now. This could make the document quite smaller, since we would not document the old get/setXXX methods and it would help the adoption of the new API. Users interested in the older parts could check an older version of the API doc (2.42, for example), that would be kept along with the new docs.

Focus

Meeting Results

  • remove deprecated items from next version of doc.
  • create User Guide with examples
  • details to be finalized later

Our main document is an API Reference Guide. Its purpose is to list everything available in the BPython API, but it also contains many code samples and we try to explain things well.

Still, many users complain that it is not user-friendly enough, that it needs more examples, a more gentle introduction, whatever.

Should we keep trying to make this doc "nicer" (and so, bigger) for new users, as if it were the mother of all bpy resources or is it better to split things and have an API reference plus other doc(s) to help teaching BPy? There are other online resources, like the Summer of Documentation text on BPython, sites, wiki, etc. that can help.

--

  • A traditional style for documenting a library or API is to create a Reference Manual and a User Guide. The User Guide provides an introduction to the API with examples and tutorials while the Reference Guide provides the full details.
Our current BPy docs combine both in the same document. From the user viewpoint, we would be better off splitting our doc into two separate but complimentary parts. The User Guide will contain our examples. The Reference Guiide will contain the modules and classe hierarchy. --Stivs 08:18, 24 March 2007 (CET)

Programming Model For Error Handling

Meeting Results:

  • exceptions good
  • return codes bad


Programming models for error handling come in two basic flavors: One is based on return codes, the other on exceptions.

  • Return Codes - Each function call returns a status code or special value to indicate whether the call succeeded. Programming in this environment is like a mountain climber negotiating a glacier where each step is carefully tested. This style is well suited for systems programming or system integration where the return types are not uniform. Passing values back up thru layers of calls is difficult however.
  • Exceptions - Programming with exceptions is more like a walk in the park where everything is assumed to be OK all the time. Functions always return useful values or they raise an exception. This style is more suited for a scripting language. The scripter does not need to remember details like whether a return value of zero means success or failure. It is never necessary to check if a returned value is valid.

It also makes it easier to pass errors back up thru a chain of function calls.

In the BPy project, we have decided to support exceptions. This means methods always return useful data or they raise an exeption. The user never needs to check a returned value to see if an error occurred.

In addition to making it easier to pass errors up thru a call chain, exceptions can be used to signal an error in methods that do not naturally return a value.

Something currently missing from our epydoc is the use of the @raise tag to document methods that throw exceptions. --Stivs 15:39, 25 March 2007 (CEST)