Since this org-babel + tramp-cache incompatibility is very puzzling, I continued researching it. The line that makes my Python block stop working (i.e. outputting None instead of the "x" I asked with print "x') is this one, found in tramp-cache.el:
(add-hook 'kill-buffer-hook 'tramp-flush-file-function)
Its code is:
(defun tramp-flush-file-function ()
"Flush all Tramp cache properties from `buffer-file-name'.
This is suppressed for temporary buffers."
(unless (string-match "^ \\*temp\\*" (or (buffer-name) ""))
(let ((bfn (if (stringp (buffer-file-name))
(buffer-file-name)
default-directory)))
(when (tramp-tramp-file-p bfn)
(with-parsed-tramp-file-name bfn nil
(tramp-flush-file-property v localname))))))
That „temporary buffer detector“ is working correctly because org-babel buffers have names like " *temp*-993012", which are correctly detected.
I'm afraid that the (string-match …) will forget the last search, so later (match-string) done by babel will be from the wrong search. Can this happen?