matrix_basis isn't affected correctly

I'd like to set the matrix_basis of an object. Unfortunately, the matrix isn't get updated correctly.

This is my test scene, consisting of an plane and a cylinder object: My objective is to get alpha, the plane's x angle, and apply it in some way to cylinder.

Here is the code:

import bpy
from math import *
from mathutils import *

plane = bpy.data.objects['plane']
cylinder = bpy.data.objects['cylinder']

alpha = plane.rotation_euler # x rotation
print('alpha=%.2f' % degrees(alpha))

'''
# normal rotation matrix around x axis
# works fine
m = Matrix(( \
[1, 0, 0, 0], \
[0, cos(alpha), -sin(alpha), 0], \
[0, sin(alpha), cos(alpha), 0], \
[0, 0, 0, 1] \
))
'''

m = Matrix(( \
[1, 0, 0, 0], \
[0, cos(alpha), 0, 0], \
[0, sin(alpha), 1, 0], \
[0, 0, 0, 1] \
))

cylinder.matrix_basis = m
bpy.context.scene.update() # is this call needed?

# prints different matrix than m
print(cylinder.matrix_basis)

# for debugging
print(cylinder.matrix_world)

This is the console output of it:

alpha=-17.10
<Matrix 4x4 (1.0000,  0.0000, 0.0000, 0.0000)
(0.0000,  0.9890, 0.1478, 0.0000)
(0.0000, -0.1478, 0.9890, 0.0000)
(0.0000,  0.0000, 0.0000, 1.0000)>
<Matrix 4x4 (1.0000,  0.0000, 0.0000, 0.0000)
(0.0000,  0.9890, 0.1478, 0.0000)
(0.0000, -0.1478, 0.9890, 0.0000)
(0.0000,  0.0000, 0.0000, 1.0000)>

The first printed matrix is the matrix_basis. As you can see, it differs from m. For example, the element of the second row, third column have to be 0 but is 0.1478.

I haven't apply any constraints to cylinder. So whats wrong by setting the basis_matrix in this way?

Edit:

In addtion to @mont29 's answer, I've found the following document: issues in orientation-matrix in Blender <2.50

Current implementation of orientation-matrix system in Blender is not reliable: there are limitations / some nasty problems with mirrored/non-uniform-scaled geometry in Blender

Perhaps, it is not a bug, but a system design side-effect: insufficient/incomplete representation method of object orientation data with "all-in-one loc/rot/scale-matrix".

Yes, m isn't orthogonal (m.is_orthogonal == False), so Blender can't handle it.

matrix_basis only supports valid transform matrices - not a specialist, but by the look of it your m looks more like a shearing one?

Note that internally, there is no matrix_basis at all, Blender only stores translation/rotation/scaling separately, and converts them to/from RNA's matrix_basis. So if a matrix cannot be represented by a translation, rotation and scaling, then you cannot expect it to work.

As @mont29 answered, your matrix produces a shear transformation, and those are not supported by Blender (at least in 2.91 as of writing this answer).

What you can do is shear the mesh data and reset the transform, if you can afford losing the latter. To match your sample code above, the following lines would help already:

cylinder.data.transform(m)  # data is the Mesh
cylinder.data.matrix_world = Matrix.Identity(4)

I had the same issue importing model data with transformation matrices exported by another DCC tool in which some meshes were sheared. I first checked if their transformation matrix contained a shear by them not being orthogonal, and then applied the shear on the mesh data, only retaining the translation in that case:

from bpy import context as C

# A sample sheared matrix, as loaded from file or whatever
m = Matrix((
(-10.5924, -0.0638, -0.7292, 666.0000),
(  8.0741,  0.0487, -0.9566, 818.0000),
( -0.0297, -1.8306,  0.0000,  51.0000),
(  0.0000,  0.0000,  0.0000,   1.0000)))

# A matrix is sheared if it is not orthogonal.
m33 = m.to_3x3()
if m33.is_orthogonal:  # allow only rotation
# ... or ...
if m33.is_orthogonal_axis_vectors:  # allow rotation and scale
# Matrix is not sheared, can be represented directly in Blender
C.object.matrix_world = m
else:
# Matrix is sheared, apply shear to mesh data.
C.object.data.transform(m33.to_4x4())
# Only use translation in world matrix.
C.object.matrix_world = m.translation