I think I may have sorted this now using Vector.project. For the script to work one would need to create a 2 vertex edge and call it line as well as adding an empty that is renamed empty (not Empty):
import bpy
from mathutils import Matrix
empty = bpy.data.objects["empty"]
line = bpy.data.objects["line"]
mw = line.matrix_world
ev = empty.location
# get the two line points in world space
lv0 = mw * line.data.vertices[0].co
lv1 = mw * line.data.vertices[1].co
# get the vector of the line
lv = (lv0 - lv1)
# get the translation between the original line and the line vector
# and apply that to the empty location to get the location of the
# empty relative to the line vector
evt = Matrix.Translation(lv - lv0) * ev
# get the projection of the new empty location onto the line vector
# - `project` gets the perpendicular vector where `evt` intersects `lv`
evp = evt.project(lv)
# get the translation between the projected vector and the empty
tm = Matrix.Translation(evp - evt).inverted()
# now move the line
line.location = tm * line.location
I have created a simple add-on on GitHub to demonstrate the behaviour.