Failed Conditions
Pull Request — master (#1569)
by Abdeali
01:55
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
import platform
0 ignored issues
show
Unused Code introduced by
The import platform seems to be unused.
Loading history...
2
from contextlib import contextmanager
3
from subprocess import PIPE, Popen
4
5
from coalib.parsing.StringProcessing import escape
6
7
8
@contextmanager
9
def run_interactive_shell_command(command, **kwargs):
10
    """
11
    Runs a command in shell and provides stdout, stderr and stdin streams.
12
13
    This function creates a context manager that sets up the process, returns
14
    to caller, closes streams and waits for process to exit on leaving.
15
16
    The process is opened in `universal_newlines` mode.
17
18
    :param command: The command to run on shell.
19
    :param kwargs:  Additional keyword arguments to pass to `subprocess.Popen`
20
                    that is used to spawn the process (except `shell`,
21
                    `stdout`, `stderr`, `stdin` and `universal_newlines`, a
22
                    `TypeError` is raised then).
23
    :return:        A context manager yielding the process started from the
24
                    command.
25
    """
26
    process = Popen(command,
27
                    shell=True,
28
                    stdout=PIPE,
29
                    stderr=PIPE,
30
                    stdin=PIPE,
31
                    universal_newlines=True,
32
                    **kwargs)
33
    try:
34
        yield process
35
    finally:
36
        process.stdout.close()
37
        process.stderr.close()
38
        process.stdin.close()
39
        process.wait()
40
41
42
def run_shell_command(command, stdin=None, **kwargs):
43
    """
44
    Runs a command in shell and returns the read stdout and stderr data.
45
46
    This function waits for the process to exit.
47
48
    :param command: The command to run on shell.
49
    :param stdin:   Initial input to send to the process.
50
    :param kwargs:  Additional keyword arguments to pass to `subprocess.Popen`
51
                    that is used to spawn the process (except `shell`,
52
                    `stdout`, `stderr`, `stdin` and `universal_newlines`, a
53
                    `TypeError` is raised then).
54
    :return:        A tuple with `(stdoutstring, stderrstring)`.
55
    """
56
    with run_interactive_shell_command(command, **kwargs) as p:
57
        ret = p.communicate(stdin)
58
    return ret
59
60
61
def get_shell_type():  # pragma: no cover
62
    """
63
    Finds the current shell type based on the outputs of common pre-defined
64
    variables in them. This is useful to identify which sort of escaping
65
    is required for strings.
66
67
    :return: The shell type. This can be either "powershell" if Windows
68
             Powershell is detected, "cmd" if command prompt is been
69
             detected or "sh" if it's neither of these.
70
    """
71
    out_hostname, _ = run_shell_command(["echo", "$host.name"])
72
    if out_hostname.strip() == "ConsoleHost":
73
        return "powershell"
74
    out_0, _ = run_shell_command(["echo", "$0"])
75
    if out_0.strip() == "" and out_0.strip() == "":
76
        return "cmd"
77
    return "sh"
78
79
80
def prepare_string_argument(string, shell=get_shell_type()):
81
    """
82
    Prepares a string argument for being passed as a parameter on shell.
83
84
    On `sh` this function effectively encloses the given string
85
    with quotes (either '' or "", depending on content).
86
87
    :param string: The string to prepare for shell.
88
    :param shell:  The shell platform to prepare string argument for.
89
                   If it is not "sh" they will be ignored and return the
90
                   given string without modification.
91
    :return:       The shell-prepared string.
92
    """
93
    if shell == "sh":
94
        return '"' + escape(string, '"') + '"'
95
    else:
96
        return string
97
98
99
def escape_path_argument(path, shell=get_shell_type()):
100
    """
101
    Makes a raw path ready for using as parameter in a shell command (escapes
102
    illegal characters, surrounds with quotes etc.).
103
104
    :param path:  The path to make ready for shell.
105
    :param shell: The shell platform to escape the path argument for. Possible
106
                  values are "sh", "powershell", and "cmd" (others will be
107
                  ignored and return the given path without modification).
108
    :return:      The escaped path argument.
109
    """
110
    if shell == "cmd":
111
        # If a quote (") occurs in path (which is illegal for NTFS file
112
        # systems, but maybe for others), escape it by preceding it with
113
        # a caret (^).
114
        return '"' + escape(path, '"', '^') + '"'
115
    elif shell == "sh":
116
        return escape(path, " ")
117
    else:
118
        # Any other non-supported system doesn't get a path escape.
119
        return path
120