I spent a few hours chipping away at this yesterday, and made a bit of progress so I'll share that below along with some suggestions moving forward.
First, it seems that we can certainly rotate and translate the bounding box (bbox) or frame around the legend. In the first example below you can see that a transform
can be applied, albeit requiring some oddly large translation numbers after applying the 90 degree rotation. But, there are actually problems saving the translated legend frame to an image file so I had to take a screenshot from the IPython notebook. I've added some comments as well.
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import matplotlib.transforms
fig = plt.figure()
ax = fig.add_subplot('121') #make room for second subplot, where we are actually placing the legend
ax2 = fig.add_subplot('122') #blank subplot to make space for legend
ax2.axis('off')
ax.plot([4,5,6], label = 'test')
transform = matplotlib.transforms.Affine2D(matrix=np.eye(3)) #start with the identity transform, which does nothing
transform.rotate_deg(90) #add the desired 90 degree rotation
transform.translate(410,11) #for some reason we need to play with some pretty extreme translation values to position the rotated legend
legend = ax.legend(bbox_to_anchor=[1.5,1.0])
legend.set_title('test title')
legend.get_frame().set_transform(transform) #This actually works! But, only for the frame of the legend (see below)
frame = legend.get_frame()
fig.subplots_adjust(wspace = 0.4, right = 0.9)
fig.savefig('rotate_legend_1.png',bbox_extra_artists=(legend,frame),bbox_inches='tight', dpi = 300) #even with the extra bbox parameters the legend frame is still getting clipped

Next, I thought it would be smart to explore the get_methods()
of other legend components. You can sort of dig through these things with dir(legend)
and legend.__dict__
and so on. In particular, I noticed that you can do this: legend.get_title().set_transform(transform)
, which would seem to imply that we could translate the legend text (and not just the frame as above). Let's see what happens when I tried that:
fig2 = plt.figure()
ax = fig2.add_subplot('121')
ax2 = fig2.add_subplot('122')
ax2.axis('off')
ax.plot([4,5,6], label = 'test')
transform = matplotlib.transforms.Affine2D(matrix=np.eye(3))
transform.rotate_deg(90)
transform.translate(410,11)
legend = ax.legend(bbox_to_anchor=[1.5,1.0])
legend.set_title('test title')
legend.get_frame().set_transform(transform)
legend.get_title().set_transform(transform) #one would expect this to apply the same transformation to the title text in the legend, rotating it 90 degrees and translating it
frame = legend.get_frame()
fig2.subplots_adjust(wspace = 0.4, right = 0.9)
fig2.savefig('rotate_legend_1.png',bbox_extra_artists=(legend,frame),bbox_inches='tight', dpi = 300)

The legend title seems to have disappeared in the screenshot from the IPython notebook. But, if we look at the saved file the legend title is now in the bottom left corner and seems to have ignored the rotation component of the transformation (why?):

I had similar technical difficulties with this type of approach:
bbox = matplotlib.transforms.Bbox([[0.,1],[1,1]])
trans_bbox = matplotlib.transforms.TransformedBbox(bbox, transform)
legend.set_bbox_to_anchor(trans_bbox)
Other notes and suggestions:
- It might be a sensible idea to dig into the differences in behaviour between the legend title and frame objects--why do they both accept transforms, but only the frame accepts a rotation? Perhaps it would be possible to subclass the legend object in the source code and make some adjustments.
- We also need to find a solution for the rotated / translated legend frame not being saved to output, even after following various related suggestion on SO (i.e., Matplotlib savefig with a legend outside the plot).