# Making a chain of bones without bpy.ops

While making mesh objects with Python, I felt encouraged by the community to avoid using bpy.ops, as it is less efficient. I am now making armature objects. I am making a kind of snake where the body will simply follow a curve, but the 'neck' will have a 2 stage IK rig with two control bones. This code makes one neck. The def contains my effort so far, based on what I learned from meshes. I'm still working in 2.83 due to other code I have. I have got this far with bpy.ops and could carry on, but I would like to know the bones better.

import bpy

# Rig A
xlate = -0.01
location=(-0.009134, 0.25, 0.0),
rotation=(pi/2, 0.0, rot_z))
ob_a = bpy.data.objects.get('Armature')

# Make a chain of bones, including some pose angles while the
# extrude bone is selected
bpy.ops.object.mode_set(mode='EDIT')
for i in range(45):
bpy.ops.armature.extrude_move(
TRANSFORM_OT_translate={"value":(0, xlate, 0)})
bpy.ops.transform.rotate(value=rot_z, orient_axis='Z')
if i > 40:
bpy.ops.transform.rotate(value=rot_x, orient_axis='X')

arm_a = bpy.data.armatures.get('Armature')
# deselect the last extruded bone's tail, works in edit mode!
bn = arm_a.edit_bones[-1]
bn.select_tail = False

# Make a copy of two bones for use as control bones later,
# one halfway along the chain, one near the end
control_neck = arm_a.edit_bones.get("Bone.020")

arm_a.edit_bones.active = control_neck
control_neck.select = True

# must select tail in object mode
bpy.ops.object.mode_set(mode='OBJECT')
control_neck.select_tail = True
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.armature.duplicate_move(
TRANSFORM_OT_translate={"value":(0.0, 0.0, 0.02)})

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


I have tried without bpy.ops and come to a halt.

import bpy
from mathutils import Matrix, Vector

def new_armature(name):
arm = bpy.data.armatures.new(name)
obj = bpy.data.objects.new(name, arm)

return obj

# Rig A
xlate = -0.01

ob = new_armature('Armature')
ob.location = (-0.009134, 0.25, 0.0)
ob.rotation_euler = (pi/2, 0.0, rot_z)
ob.scale = (0.0125, 0.0125, 0.0125)
bpy.context.view_layer.objects.active = ob
ob.select_set(True)
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
edit_bones = ob.data.edit_bones
identity = Matrix()
for i in range(12):
bone_name = 'b.0' if i < 10 else 'b.'
b = edit_bones.new(bone_name + str(i))
b.tail = (0.0, 0.0, 1.0)
if i:
b.transform(Matrix.Translation((0,0,1)))# just a stab in the dark
b.parent = prev
b.tail[2] = prev.tail[2] + 1.0
b.use_relative_parent = True
prev = b
bpy.ops.object.mode_set(mode='OBJECT')
# Parenting looks ok in Outliner, but bones don't seem connected


I am obviously missing something, or need pointing to the next step. The bpy.ops bones are connected when grabbed (G) in edit mode, but the other code they are not.

My bpy.ops code works ok; note however that 'duplicate_move' only works if a bone AND its tail are selected, and the tail has to be selected in object mode. Nevertheless I found this was easier than adding new single bones, and orienting/scaling them to the desired bone positions.

As far as I know, with running bpy.ops.armature.duplicate() the new bones are connected by default. Not so when using edit_bones.new(). So insert b.use_connect = True at the end of the if i: block to connect bones with parents.