So here is a PyMEL script to attach a locator on to a surface using pointOnSurfaceInfo and a 4x4 matrix.
This is going to replace my heavy use of follicles!
Hat tip to Vasil Shotarov who already wrote about this: Maya matrix nodes - Part 3: Matrix rivet | bindpose
(edit: changed .local to .worldSpace)
import pymel.core as pm
nurbs = pm.PyNode('ribbon_geo')
uPos = 0.2
vPos = 0.6
pointOnSurface = pm.createNode('pointOnSurfaceInfo')
nurbs.getShape().worldSpace.connect(pointOnSurface.inputSurface)
pointOnSurface.parameterU.set(uPos)
pointOnSurface.parameterV.set(vPos)
result = pm.spaceLocator(n='result')
# Compose a 4x4 matrix
mtx = pm.createNode('fourByFourMatrix')
outMatrix = pm.createNode('decomposeMatrix')
mtx.output.connect(outMatrix.inputMatrix)
outMatrix.outputTranslate.connect(result.translate)
outMatrix.outputRotate.connect(result.rotate)
'''
Thanks to kiaran at https://forums.cgsociety.org/t/rotations-by-surface-normal/1228039/4
# Normalize these vectors
[tanu.x, tanu.y, tanu.z, 0]
[norm.x, norm.y, norm.z, 0]
[tanv.x, tanv.y, tanv.z, 0]
# World space position
[pos.x, pos.y, pos.z, 1]
'''
pointOnSurface.normalizedTangentUX.connect(mtx.in00)
pointOnSurface.normalizedTangentUY.connect(mtx.in01)
pointOnSurface.normalizedTangentUZ.connect(mtx.in02)
mtx.in03.set(0)
pointOnSurface.normalizedNormalX.connect(mtx.in10)
pointOnSurface.normalizedNormalY.connect(mtx.in11)
pointOnSurface.normalizedNormalZ.connect(mtx.in12)
mtx.in13.set(0)
pointOnSurface.normalizedTangentVX.connect(mtx.in20)
pointOnSurface.normalizedTangentVY.connect(mtx.in21)
pointOnSurface.normalizedTangentVZ.connect(mtx.in22)
mtx.in23.set(0)
pointOnSurface.positionX.connect(mtx.in30)
pointOnSurface.positionY.connect(mtx.in31)
pointOnSurface.positionZ.connect(mtx.in32)
mtx.in33.set(1)