How to get back to bone in hierarchy

I am trying to build an arm and hand using python, however, upon completing the extrusions
from the shoulder to the tip of the first finger, (the pinky), I can not seem to figure out
how to get back to joint for the wrist, select the tip, and start the extrusion process
again for making the ring finger. Here is the runnable code:

import bpy
bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.cursor_location = (.2, 0, 3)  # x z y
# Arm
bpy.ops.object.armature_add()
bpy.ops.object.mode_set(mode='EDIT')
bpy.data.armatures["Armature"].bones["Bone"].select_head=False
bpy.data.armatures["Armature"].bones["Bone"].select_tail=True
bpy.ops.transform.translate(value=(0, 0, -0.56), constraint_axis=(False, False, True), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=True)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.transform.rotate(value=1.57, axis=(-0, 1, 0), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.6, .0, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.6, .0, 0)}) # xzy
# Wrist and pinky
#bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.06, .0, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.03, .08, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.08, 0, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.08, 0, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.04, 0, 0)}) # xzy

#Here is where I need to get back to the wrist 
# and then start with the extrude command below:
#bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.04, 0, 0)})

I have tried using

bpy.ops.armature.select_hierarchy(direction='PARENT', extend=False)

multiple times to get back to the right armature, and that seems to work, but then when I try to
extrude, it extrudes on both ends of the armature. I believe I need to select the bone in that armature
and then select the tail, and have tried numerous combinations of code like


# Edit mode
bpy.data.armatures["Armature"].bones["Bone"].select_head=True
bpy.data.armatures["Armature"].bones["Bone"].select_tail=False
# object mode
ob.select_tail = True
ob.select_head = False

bla bla bla, and nothing seems to work. Does anyone know how to do this?

well, you should set:

Bone.select = False
Bone.select_head = False
Bone.select_tail = True

But actually, you shouldn’t use operators for all this, but low-level API. See e.g. http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Cookbook/Code_snippets/Armatures

That did not work. I included the code so anyone who thinks they have an answer can run it and see if it works before they post an answer. Thanks for the link though, I am looking it over, as it may be helpful. I am having a little trouble interpreting it though, as I am new to python (use to use MEL) and this code has the functions all mixed in, making it harder to see what the actual flow of events is . . .

I am also looking over this link http://graemehill.ca/exporting-armature-animation-with-the-blender-25-python-api/
which seems to have some relevant info.

Additionally, I am not so sure what you mean by “low-level API”, I’m using:

bpy.ops.object.armature_add()

and

bpy.ops.armature.extrude_move

The code on the link is using:

bpy.ops.object.add(type='ARMATURE', enter_editmode=True, location=origin)

They are both from

bpy.ops.object

Also, besides the ancient “Blender 2.49 Scripting” book written back in 1850, are there any newer books or sources for
learning scripting in Blender?

the most important source are the API docs:
http://www.blender.org/documentation/250PythonDoc/

bpy.ops.object.add(type=‘ARMATURE’, enter_editmode=True, location=origin)

That one is indeed not low-level as well, and you could use different approach here. But since you wanna access EditBones and not Bones, you would have to go to EditMode anyway, so it’s ok to use object.add operator here as it allows you to enter editmode after object creation.

However, I’d consider to use low-level functions to do the bone orientations/relationships. For example:

import bpyfrom mathutils import Vector


bpy.ops.object.add(type='ARMATURE', enter_editmode=True, location=(1,2,3))
ob = bpy.context.object


arm = ob.data


b1 = arm.edit_bones.new("Bone1")
vec = Vector((0,1,2))
b1.head = vec
b1.tail = vec + Vector((3,3,-3)) # example of how to set tail relative to head


b2 = arm.edit_bones.new("Bone2")
b2.tail = b1.tail - b1.head + Vector((3,2,1)) # some vector math
b2.use_connect = True # this bone's head at parent bone's tail location
b2.parent = b1


bpy.ops.object.mode_set(mode='OBJECT')

About your script:
the problem with select_hierarchy is, that it selects an entire bone, which means select, select_head and select_tail of this bone will be True, and since this bone’s == parent bone’s tail 'cause they are connected, select_tail of parent bone will be True as well. Thus, you get two bones extruded, just like in viewport with a full bone selected.

You could solve this e.g. like this:

import bpybpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.cursor_location = (.2, 0, 3)  # x z y
# Arm
bpy.ops.object.armature_add()
bpy.ops.object.mode_set(mode='EDIT')
bpy.data.armatures["Armature"].bones["Bone"].select_head=False
bpy.data.armatures["Armature"].bones["Bone"].select_tail=True
bpy.ops.transform.translate(value=(0, 0, -0.56), constraint_axis=(False, False, True), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), texture_space=False, release_confirm=True)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.transform.rotate(value=1.57, axis=(-0, 1, 0), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.6, .0, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.6, .0, 0)}) # xzy
# Wrist and pinky
#bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.06, .0, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.03, .08, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.08, 0, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.08, 0, 0)}) # xzy
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.04, 0, 0)}) # xzy

bpy.ops.armature.select_hierarchy(direction='PARENT', extend=False)
bpy.ops.armature.select_hierarchy(direction='PARENT', extend=False)

bone = bpy.context.active_bone # this is the bone we are interested in, was selected by select_hierarchy
bpy.ops.armature.select_all(action='DESELECT') # clear selection
bone.select_tail = True # re-select the bone we wanna extrude, but only its tail!
bpy.ops.armature.extrude_move(ARMATURE_OT_extrude=None, TRANSFORM_OT_translate={"value":(.04, 0, 0)})