I'm now using brute force...
def angle(pointlist):
# calculates using cosine rule: A^2 = B^2 + C^2 - 2BC cos a
# a = arccos ((B^2+C^2-A^2)/(2BC))
# but multiply by 180/pi to convert from radians
A_vector = pointlist[0] - pointlist[2]
B_vector = pointlist[0] - pointlist[1]
C_vector = pointlist[1] - pointlist[2]
A2,B2,C2 = map(square_length_of_vector,(A_vector,B_vector,C_vector))
B,C = map(numpy.sqrt,(B2,C2))
side_ratio = (B2+C2-A2)/(2*B*C)
if side_ratio > 1: # possible on sharp angle due to rounding errors
return 0.
elif side_ratio < -1: # possible on blunt angle due to rounding errors
return 180.
else:
return 180/numpy.pi * numpy.arccos(side_ratio)
...shame if arc doesn't have a way of doing that that generalises to spherical geometry, though.