Failed Conditions
Pull Request — master (#1228)
by Mischa
01:44
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
    This function encloses the given string with quotes (either '' or "",
13
    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
                   values are "Windows" and "Linux" (others will be ignored and
18
                   return the given string without modification).
19
    :return:       The shell-prepared string.
20
    """
21
    if os == "Windows":
22
        return '"' + escape(escape(string, '\n', '^\n'), '"', '^') + '"'
23
    elif os == "Linux":
24
        return '"' + escape(string, '"') + '"'
25
    else:
26
        return string
27
28
29
def escape_path_argument(path, os=platform.system()):
30
    """
31
    Makes a raw path ready for using as parameter in a shell command (escapes
32
    illegal characters, surrounds with quotes etc.).
33
34
    :param path: The path to make ready for shell.
35
    :param os:   The shell platform to escape the path argument for. Possible
36
                 values are "Windows" and "Linux" (others will be ignored and
37
                 return the given path without modification).
38
    :return:     The escaped path argument.
39
    """
40
    if os == "Windows":
41
        # If a quote (") occurs in path (which is illegal for NTFS file
42
        # systems, but maybe for others), escape it by preceding it with
43
        # a caret (^).
44
        return '"' + escape(path, '"', '^') + '"'
45
    elif os == "Linux":
46
        return escape(path, " ")
47
    else:
48
        # Any other non-supported system doesn't get a path escape.
49
        return path
50
51
52
@contextmanager
53
def run_interactive_shell_command(command, **kwargs):
54
    """
55
    Runs a command in shell and provides stdout, stderr and stdin streams.
56
57
    This function creates a context manager that sets up the process, returns
58
    to caller, closes streams and waits for process to exit on leaving.
59
60
    The process is opened in `universal_newlines` mode.
61
62
    :param command: The command to run on shell.
63
    :param kwargs:  Additional keyword arguments to pass to `subprocess.Popen`
64
                    that is used to spawn the process (except `shell`,
65
                    `stdout`, `stderr`, `stdin` and `universal_newlines`, a
66
                    `TypeError` is raised then).
67
    :return:        A context manager yielding the process started from the
68
                    command.
69
    """
70
    process = Popen(command,
71
                    shell=True,
72
                    stdout=PIPE,
73
                    stderr=PIPE,
74
                    stdin=PIPE,
75
                    universal_newlines=True,
76
                    **kwargs)
77
    try:
78
        yield process
79
    finally:
80
        process.stdout.close()
81
        process.stderr.close()
82
        process.stdin.close()
83
        process.wait()
84
85
86
def run_shell_command(command, input=None, **kwargs):
87
    """
88
    Runs a command in shell and returns the read stdout and stderr data.
89
90
    This function waits for the process to exit.
91
92
    :param command: The command to run on shell.
93
    :param input:   Initial input to send to the process.
94
    :param kwargs:  Additional keyword arguments to pass to `subprocess.Popen`
95
                    that is used to spawn the process (except `shell`,
96
                    `stdout`, `stderr`, `stdin` and `universal_newlines`, a
97
                    `TypeError` is raised then).
98
    :return:        A tuple with `(stdoutstring, stderrstring)`.
99
    """
100
    with run_interactive_shell_command(command, **kwargs) as p:
101
        ret = p.communicate(input)
102
    return ret
103