Completed
Pull Request — master (#1228)
by Mischa
01:24
created

coalib.misc.run_interactive_shell_command()   B

Complexity

Conditions 1

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 32
rs 8.8571
1
from contextlib import contextmanager
2
import platform
3
from subprocess import Popen, PIPE
4
5
from coalib.parsing.StringProcessing import escape
6
7
8
def prepare_string_argument(string, os=platform.system()):
9
    """
10
    Prepares a string argument for being passed as a parameter on shell.
11
12
    On Linux and Darwin this function effectively encloses the given string
13
    with quotes (either '' or "", depending on content).
14
15
    :param string: The string to prepare for shell.
16
    :param os:     The shell platform to prepare string argument for. Possible
17
                   "Linux" and "Darwin" (others will be ignored and return the
18
                   given string without modification).
19
    :return:       The shell-prepared string.
20
    """
21
    if os == "Linux" or os == "Darwin":
22
        return '"' + escape(string, '"') + '"'
23
    else:
24
        return string
25
26
27
def escape_path_argument(path, os=platform.system()):
28
    """
29
    Makes a raw path ready for using as parameter in a shell command (escapes
30
    illegal characters, surrounds with quotes etc.).
31
32
    :param path: The path to make ready for shell.
33
    :param os:   The shell platform to escape the path argument for. Possible
34
                 values are "Windows", "Linux" and "Darwin" (others will be
35
                 ignored and return the given path without modification).
36
    :return:     The escaped path argument.
37
    """
38
    if os == "Windows":
39
        # If a quote (") occurs in path (which is illegal for NTFS file
40
        # systems, but maybe for others), escape it by preceding it with
41
        # a caret (^).
42
        return '"' + escape(path, '"', '^') + '"'
43
    elif os == "Linux" or os == "Darwin":
44
        return escape(path, " ")
45
    else:
46
        # Any other non-supported system doesn't get a path escape.
47
        return path
48
49
50
@contextmanager
51
def run_interactive_shell_command(command, **kwargs):
52
    """
53
    Runs a command in shell and provides stdout, stderr and stdin streams.
54
55
    This function creates a context manager that sets up the process, returns
56
    to caller, closes streams and waits for process to exit on leaving.
57
58
    The process is opened in `universal_newlines` mode.
59
60
    :param command: The command to run on shell.
61
    :param kwargs:  Additional keyword arguments to pass to `subprocess.Popen`
62
                    that is used to spawn the process (except `shell`,
63
                    `stdout`, `stderr`, `stdin` and `universal_newlines`, a
64
                    `TypeError` is raised then).
65
    :return:        A context manager yielding the process started from the
66
                    command.
67
    """
68
    process = Popen(command,
69
                    shell=True,
70
                    stdout=PIPE,
71
                    stderr=PIPE,
72
                    stdin=PIPE,
73
                    universal_newlines=True,
74
                    **kwargs)
75
    try:
76
        yield process
77
    finally:
78
        process.stdout.close()
79
        process.stderr.close()
80
        process.stdin.close()
81
        process.wait()
82
83
84
def run_shell_command(command, stdin=None, **kwargs):
85
    """
86
    Runs a command in shell and returns the read stdout and stderr data.
87
88
    This function waits for the process to exit.
89
90
    :param command: The command to run on shell.
91
    :param stdin:   Initial input to send to the process.
92
    :param kwargs:  Additional keyword arguments to pass to `subprocess.Popen`
93
                    that is used to spawn the process (except `shell`,
94
                    `stdout`, `stderr`, `stdin` and `universal_newlines`, a
95
                    `TypeError` is raised then).
96
    :return:        A tuple with `(stdoutstring, stderrstring)`.
97
    """
98
    with run_interactive_shell_command(command, **kwargs) as p:
99
        ret = p.communicate(stdin)
100
    return ret
101