1. Problem

EIN uses async call to run the cod block in org mode. This prevents Emacs from being blocked but then it breaks the :post header to not work because it only processes the placeholder text. After the call finishes, the result will overwrite the previously processed placeholder result.

2. solution

import re

def gen_image_link(data):
    
    html_tag = r'<figure><image src="ein-images/\\1" alt="\\1"></figure>'

    import re

    regex = r"\[\[file:.*/(.*)\]\]"

    # You can manually specify the number of replacements by changing the 4th argument
    result = re.sub(regex, html_tag, data, 0, re.MULTILINE)

    return result
    
print("<pre>")
print(gen_image_link(data))
print("</pre>")
cutoffs = {}
# print("test")
for i in "ab":
    cutoffs[i] = 100
    print("{} cutoff number at 99%: {}".format(i, cutoffs[i]))
<pre>
a cutoff number at 99%: 100
b cutoff number at 99%: 100

</pre>
cutoffs = {}
for i in "ab":
    cutoffs[i] = 100
    print("{} cutoff number at 99%: {}".format(i, str(cutoffs[i])))
# for i in range(2):
#     print("[[file:ein-images/ob-ein-084c59567740bbf1e65c473696b8c9f6.png]]")
a cutoff number at 99%: 100 b cutoff number at 99%: 100
cutoffs = {}
for i in "ab":
    cutoffs[i] = 100
    print("{} cutoff number at 99%: {}".format(i, cutoffs[i]))
# for i in range(2):
#     print("[[file:ein-images/ob-ein-084c59567740bbf1e65c473696b8c9f6.png]]")
a cutoff number at 99%: 100
b cutoff number at 99%: 100

(defun ob-ein--execute-async-callback (buffer params result-type result-params name)
  "Return callback of 1-arity (the shared output cell) to update org buffer when
`ein:shared-output-eval-string' completes.

The callback returns t if results containt RESULT-TYPE outputs, nil otherwise."
  (apply-partially
   (lambda (buffer* params* result-type* result-params* name* cell)
     (when-let ((raw (aif (ein:oref-safe cell 'traceback)
                         (ansi-color-apply (ein:join-str "\n" it))
                       (ob-ein--process-outputs result-type* cell params*))))
       (prog1 t
         (let ((result
                (let ((tmp-file (org-babel-temp-file "ein-")))
                  (with-temp-file tmp-file (insert raw))
                  (org-babel-result-cond result-params*
                    raw (org-babel-import-elisp-from-file tmp-file '(16)))))
               (info (org-babel-get-src-block-info 'light)))
           (ein:log 'debug "ob-ein--execute-async-callback %s \"%s\" %s"
                    name* result buffer*)
           (save-excursion
             (save-restriction
               (with-current-buffer buffer*
                 (unless (stringp (org-babel-goto-named-src-block name*)) ;; stringp=error
                   (when (version-list-< (version-to-list (org-release)) '(9))
                     (when info ;; kill #+RESULTS: (no-name)
                       (setf (nth 4 info) nil)
                       (org-babel-remove-result info))
                     (org-babel-remove-result)) ;; kill #+RESULTS: name

                   ;; Possibly perform async-post process provided its
                   ;; appropriate.  Dynamically bind "*this*" to the
                   ;; actual results of the block.
                   ;; Also need to handle the scenario when the code block from :async-post
                   ;; cannot be found.
                   (let ((async-post (cdr (assq :async-post params))))
                     (when async-post
                       (setq *this* result)
                       (setq result (org-babel-ref-resolve async-post))))
                   (org-babel-insert-result
                    result
                    (cdr (assoc :result-params
                                (cl-third (org-babel-get-src-block-info)))))
                   (org-redisplay-inline-images)))))))))
   buffer params result-type result-params name))