_shell_utils.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. """
  2. Helper functions for interacting with the shell, and consuming shell-style
  3. parameters provided in config files.
  4. """
  5. import os
  6. import shlex
  7. import subprocess
  8. try:
  9. from shlex import quote
  10. except ImportError:
  11. from pipes import quote
  12. __all__ = ['WindowsParser', 'PosixParser', 'NativeParser']
  13. class CommandLineParser:
  14. """
  15. An object that knows how to split and join command-line arguments.
  16. It must be true that ``argv == split(join(argv))`` for all ``argv``.
  17. The reverse neednt be true - `join(split(cmd))` may result in the addition
  18. or removal of unnecessary escaping.
  19. """
  20. @staticmethod
  21. def join(argv):
  22. """ Join a list of arguments into a command line string """
  23. raise NotImplementedError
  24. @staticmethod
  25. def split(cmd):
  26. """ Split a command line string into a list of arguments """
  27. raise NotImplementedError
  28. class WindowsParser:
  29. """
  30. The parsing behavior used by `subprocess.call("string")` on Windows, which
  31. matches the Microsoft C/C++ runtime.
  32. Note that this is _not_ the behavior of cmd.
  33. """
  34. @staticmethod
  35. def join(argv):
  36. # note that list2cmdline is specific to the windows syntax
  37. return subprocess.list2cmdline(argv)
  38. @staticmethod
  39. def split(cmd):
  40. import ctypes # guarded import for systems without ctypes
  41. try:
  42. ctypes.windll
  43. except AttributeError:
  44. raise NotImplementedError
  45. # Windows has special parsing rules for the executable (no quotes),
  46. # that we do not care about - insert a dummy element
  47. if not cmd:
  48. return []
  49. cmd = 'dummy ' + cmd
  50. CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
  51. CommandLineToArgvW.restype = ctypes.POINTER(ctypes.c_wchar_p)
  52. CommandLineToArgvW.argtypes = (ctypes.c_wchar_p, ctypes.POINTER(ctypes.c_int))
  53. nargs = ctypes.c_int()
  54. lpargs = CommandLineToArgvW(cmd, ctypes.byref(nargs))
  55. args = [lpargs[i] for i in range(nargs.value)]
  56. assert not ctypes.windll.kernel32.LocalFree(lpargs)
  57. # strip the element we inserted
  58. assert args[0] == "dummy"
  59. return args[1:]
  60. class PosixParser:
  61. """
  62. The parsing behavior used by `subprocess.call("string", shell=True)` on Posix.
  63. """
  64. @staticmethod
  65. def join(argv):
  66. return ' '.join(quote(arg) for arg in argv)
  67. @staticmethod
  68. def split(cmd):
  69. return shlex.split(cmd, posix=True)
  70. if os.name == 'nt':
  71. NativeParser = WindowsParser
  72. elif os.name == 'posix':
  73. NativeParser = PosixParser