editor.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/usr/bin/env python
  2. """Tools for invoking editors programmatically."""
  3. from __future__ import print_function
  4. import sys
  5. import locale
  6. import os.path
  7. import subprocess
  8. import tempfile
  9. from distutils.spawn import find_executable
  10. __all__ = [
  11. 'edit',
  12. 'get_editor',
  13. 'EditorError',
  14. ]
  15. __version__ = '1.0.3'
  16. class EditorError(RuntimeError):
  17. pass
  18. def get_default_editors():
  19. # TODO: Make platform-specific
  20. return [
  21. 'editor',
  22. 'vim',
  23. 'emacs',
  24. 'nano',
  25. ]
  26. def get_editor_args(editor):
  27. if editor in ['vim', 'gvim', 'vim.basic', 'vim.tiny']:
  28. return ['-f', '-o']
  29. elif editor == 'emacs':
  30. return ['-nw']
  31. elif editor == 'gedit':
  32. return ['-w', '--new-window']
  33. elif editor == 'nano':
  34. return ['-R']
  35. else:
  36. return []
  37. def get_editor():
  38. # Get the editor from the environment. Prefer VISUAL to EDITOR
  39. editor = os.environ.get('VISUAL') or os.environ.get('EDITOR')
  40. if editor:
  41. return editor
  42. # None found in the environment. Fallback to platform-specific defaults.
  43. for ed in get_default_editors():
  44. path = find_executable(ed)
  45. if path is not None:
  46. return path
  47. raise EditorError("Unable to find a viable editor on this system."
  48. "Please consider setting your $EDITOR variable")
  49. def get_tty_filename():
  50. if sys.platform == 'win32':
  51. return 'CON:'
  52. return '/dev/tty'
  53. def edit(filename=None, contents=None, use_tty=None):
  54. editor = get_editor()
  55. args = [editor] + get_editor_args(os.path.basename(os.path.realpath(editor)))
  56. if use_tty is None:
  57. use_tty = sys.stdin.isatty() and not sys.stdout.isatty()
  58. if filename is None:
  59. tmp = tempfile.NamedTemporaryFile()
  60. filename = tmp.name
  61. if contents is not None:
  62. with open(filename, mode='wb') as f:
  63. f.write(contents)
  64. args += [filename]
  65. stdout = None
  66. if use_tty:
  67. stdout = open(get_tty_filename(), 'wb')
  68. proc = subprocess.Popen(args, close_fds=True, stdout=stdout)
  69. proc.communicate()
  70. with open(filename, mode='rb') as f:
  71. return f.read()
  72. def _get_editor(ns):
  73. print(get_editor())
  74. def _edit(ns):
  75. contents = ns.contents
  76. if contents is not None:
  77. contents = contents.encode(locale.getpreferredencoding())
  78. print(edit(filename=ns.path, contents=contents))
  79. if __name__ == '__main__':
  80. import argparse
  81. ap = argparse.ArgumentParser()
  82. sp = ap.add_subparsers()
  83. cmd = sp.add_parser('get-editor')
  84. cmd.set_defaults(cmd=_get_editor)
  85. cmd = sp.add_parser('edit')
  86. cmd.set_defaults(cmd=_edit)
  87. cmd.add_argument('path', type=str, nargs='?')
  88. cmd.add_argument('--contents', type=str)
  89. ns = ap.parse_args()
  90. ns.cmd(ns)