I took Luc Hermitte's helpful answer and improved it somewhat.
It was returning unlisted (previously deleted) buffers, which isn't what I wanted. It now filters on buflisted(v:val), rather than on !empty(v:val).
I've added two commands:
Lssave: saves the unsorted buffer list as-is
LSsave: saves a sorted version of the buffer list
mkdir the output file's ...
Use a :terminal:
" in ~/.vim/after/ftplugin/python.vim
nnoremap <buffer> <CR> :write <bar> terminal python3 %<CR>
" update b:undo_ftplugin
When the command is finished, you can :set modifiable (:set ma) and edit.
In the end I managed to do this by checking the number of modified buffers.
Once the number of buffers reaches 0, only then run rsync.
" See https://vi.stackexchange.com/a/14313/40380
return len(filter(getbufinfo(), 'v:val.changed == 1'))
autocmd BufWritePost path/to/project/*
You may run
autocmd FileType python map <buffer> <CR> :w<CR>:call Exec('!python3 '.expand('%'))<CR>
exec printf('silent %s',a:cmd)
It writes the output of a command in a new tab.