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

coalib.misc.run_shell_command()   A

Complexity

Conditions 2

Size

Total Lines 17

Duplication

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