1. Devin Jeanpierre
  2. doctest2

Commits

Devin Jeanpierre  committed e3ae419

Added ability to communicate complex text to/from the user, which is so vital for a real interactive mode.

  • Participants
  • Parent commits 3f91b88
  • Branches interactive-mode

Comments (0)

Files changed (2)

File doctest2/interactive.py

View file
 import sys
+import os
+import tempfile
+import subprocess
 
 class ExamplePatcher:
     """
             raise ParseFailure("Could not find where header ends")
         else:
             return edited[last_header_line + len(header_divider):]
+
+TEMPFILE_PREFIX = 'doctest-interactive'
+
+def unsafe_get_edited_text(source, editor, encoding='utf-8'):
+    """
+    Send `source` (str) to a text editor to be modified by the user. Return
+    the resulting edited string.
+    
+    `editor` is a program that satisfies the requirements of $EDITOR, i.e. one
+    can invoke it as `editor foo` where foo is an absolute file path.
+    `editor` is given in the form of an argv list, e.g. ['vim', '-g'] or
+    ['emacs'] (but NOT 'vim -g' or 'emacs')
+    
+    The exact details of how this works should be that the source is saved to
+    disk, and then the editor invoked on the source, and then the source
+    read back into memory, modified.
+    
+    This is an inherently unsafe operation, as the name of the file on disk can
+    change before the editor is invoked. In addition, to support Windows, the
+    additional burden is to actually close and reopen the file again (creating
+    two race conditions). However, since two copies of the same race condition
+    isn't really worse than one, this model is also used on Unix.
+    
+    """
+    
+    with tempfile.NamedTemporaryFile(
+            mode='w',
+            encoding=encoding,
+            prefix=TEMPFILE_PREFIX,
+            delete=False) as f:
+        f.write(source)
+    
+    subprocess.call(editor + [f.name])
+    try:
+        f = open(f.name, 'r', encoding=encoding)
+        return f.read()
+    finally:
+        f.close()
+        os.unlink(f.name)

File doctest2/tests/test_interactive.py

View file
         self.assertEqual(self.warnings,
             ["warning: parse_change: header was altered."])
 
+class TestGetEditedText(unittest.TestCase):
+    editor_command = ['python', '-c', """if 1:
+        import sys
+        with open(sys.argv[1], 'w') as f:
+            f.write("success")"""]
+    
+    def test_editor_runs(self):
+        self.assertEqual(
+            interactive.unsafe_get_edited_text("failure", self.editor_command),
+            "success")
+
 if __name__ == '__main__':
     unittest.main()