In general, function calls cannot be inlined by the Python compiler because (almost) any function name may be re-bound to a different object at run time.
A very smart compiler could probably attempt to prove that no such modification can occur at run time throughout the whole program; but this is much harder than simply deciding whether inlining a given call is worth it or not.
And more to the point, PyPy can do Python inlining with its tracing JIT. The method, in both cases, is similar: find some type assumptions that are useful and usually true, generate code under those assumptions, and fall back on more generic code if they're ever false.