text: # This script uses bmesh operators to make 2 links of a chain.
  
  import bpy
  import bmesh
  import math
  import mathutils
  
  # Make a new BMesh
  bm = bmesh.new()
  
  # Add a circle XXX, should return all geometry created, not just verts.
  bmesh.ops.create_circle(
      bm,
      cap_ends=False,
      radius=0.2,
      segments=8)
  
  
  # Spin and deal with geometry on side 'a'
  edges_start_a = bm.edges[:]
  geom_start_a = bm.verts[:] + edges_start_a
  ret = bmesh.ops.spin(
      bm,
      geom=geom_start_a,
      angle=math.radians(180.0),
      steps=8,
      axis=(1.0, 0.0, 0.0),
      cent=(0.0, 1.0, 0.0))
  edges_end_a = [ele for ele in ret["geom_last"]
                 if isinstance(ele, bmesh.types.BMEdge)]
  del ret
  
  
  # Extrude and create geometry on side 'b'
  ret = bmesh.ops.extrude_edge_only(
      bm,
      edges=edges_start_a)
  geom_extrude_mid = ret["geom"]
  del ret
  
  
  # Collect the edges to spin XXX, 'extrude_edge_only' could return this.
  verts_extrude_b = [ele for ele in geom_extrude_mid
                     if isinstance(ele, bmesh.types.BMVert)]
  edges_extrude_b = [ele for ele in geom_extrude_mid
                     if isinstance(ele, bmesh.types.BMEdge) and ele.is_boundary]
  bmesh.ops.translate(
      bm,
      verts=verts_extrude_b,
      vec=(0.0, 0.0, 1.0))
  
  
  # Create the circle on side 'b'
  ret = bmesh.ops.spin(
      bm,
      geom=verts_extrude_b + edges_extrude_b,
      angle=-math.radians(180.0),
      steps=8,
      axis=(1.0, 0.0, 0.0),
      cent=(0.0, 1.0, 1.0))
  edges_end_b = [ele for ele in ret["geom_last"]
                 if isinstance(ele, bmesh.types.BMEdge)]
  del ret
  
  
  # Bridge the resulting edge loops of both spins 'a & b'
  bmesh.ops.bridge_loops(
      bm,
      edges=edges_end_a + edges_end_b)
  
  
  # Now we have made a links of the chain, make a copy and rotate it
  # (so this looks something like a chain)
  
  ret = bmesh.ops.duplicate(
      bm,
      geom=bm.verts[:] + bm.edges[:] + bm.faces[:])
  geom_dupe = ret["geom"]
  verts_dupe = [ele for ele in geom_dupe if isinstance(ele, bmesh.types.BMVert)]
  del ret
  
  # position the new link
  bmesh.ops.translate(
      bm,
      verts=verts_dupe,
      vec=(0.0, 0.0, 2.0))
  bmesh.ops.rotate(
      bm,
      verts=verts_dupe,
      cent=(0.0, 1.0, 0.0),
      matrix=mathutils.Matrix.Rotation(math.radians(90.0), 3, 'Z'))
  
  # Done with creating the mesh, simply link it into the scene so we can see it
  
  # Finish up, write the bmesh into a new mesh
  me = bpy.data.meshes.new("Mesh")
  bm.to_mesh(me)
  bm.free()
  
  
  # Add the mesh to the scene
  obj = bpy.data.objects.new("Object", me)
  bpy.context.collection.objects.link(obj)
  
  # Select and make active
  bpy.context.view_layer.objects.active = obj
  obj.select_set(True)
  