aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/ipython/py2/IPython/utils/_process_win32.py
blob: 6d7d0f419714019485ca13df5918624465b7e7db (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
"""Windows-specific implementation of process utilities. 
 
This file is only meant to be imported by process.py, not by end-users. 
""" 
 
#----------------------------------------------------------------------------- 
#  Copyright (C) 2010-2011  The IPython Development Team 
# 
#  Distributed under the terms of the BSD License.  The full license is in 
#  the file COPYING, distributed as part of this software. 
#----------------------------------------------------------------------------- 
 
#----------------------------------------------------------------------------- 
# Imports 
#----------------------------------------------------------------------------- 
from __future__ import print_function 
 
# stdlib 
import os 
import sys 
import ctypes 
 
from ctypes import c_int, POINTER 
from ctypes.wintypes import LPCWSTR, HLOCAL 
from subprocess import STDOUT 
 
# our own imports 
from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split 
from . import py3compat 
from .encoding import DEFAULT_ENCODING 
 
#----------------------------------------------------------------------------- 
# Function definitions 
#----------------------------------------------------------------------------- 
 
class AvoidUNCPath(object): 
    """A context manager to protect command execution from UNC paths. 
 
    In the Win32 API, commands can't be invoked with the cwd being a UNC path. 
    This context manager temporarily changes directory to the 'C:' drive on 
    entering, and restores the original working directory on exit. 
 
    The context manager returns the starting working directory *if* it made a 
    change and None otherwise, so that users can apply the necessary adjustment 
    to their system calls in the event of a change. 
 
    Examples 
    -------- 
    :: 
        cmd = 'dir' 
        with AvoidUNCPath() as path: 
            if path is not None: 
                cmd = '"pushd %s &&"%s' % (path, cmd) 
            os.system(cmd) 
    """ 
    def __enter__(self): 
        self.path = py3compat.getcwd() 
        self.is_unc_path = self.path.startswith(r"\\") 
        if self.is_unc_path: 
            # change to c drive (as cmd.exe cannot handle UNC addresses) 
            os.chdir("C:") 
            return self.path 
        else: 
            # We return None to signal that there was no change in the working 
            # directory 
            return None 
 
    def __exit__(self, exc_type, exc_value, traceback): 
        if self.is_unc_path: 
            os.chdir(self.path) 
 
 
def _find_cmd(cmd): 
    """Find the full path to a .bat or .exe using the win32api module.""" 
    try: 
        from win32api import SearchPath 
    except ImportError: 
        raise ImportError('you need to have pywin32 installed for this to work') 
    else: 
        PATH = os.environ['PATH'] 
        extensions = ['.exe', '.com', '.bat', '.py'] 
        path = None 
        for ext in extensions: 
            try: 
                path = SearchPath(PATH, cmd, ext)[0] 
            except: 
                pass 
        if path is None: 
            raise OSError("command %r not found" % cmd) 
        else: 
            return path 
 
 
def _system_body(p): 
    """Callback for _system.""" 
    enc = DEFAULT_ENCODING 
    for line in read_no_interrupt(p.stdout).splitlines(): 
        line = line.decode(enc, 'replace') 
        print(line, file=sys.stdout) 
    for line in read_no_interrupt(p.stderr).splitlines(): 
        line = line.decode(enc, 'replace') 
        print(line, file=sys.stderr) 
 
    # Wait to finish for returncode 
    return p.wait() 
 
 
def system(cmd): 
    """Win32 version of os.system() that works with network shares. 
 
    Note that this implementation returns None, as meant for use in IPython. 
 
    Parameters 
    ---------- 
    cmd : str or list 
      A command to be executed in the system shell. 
 
    Returns 
    ------- 
    None : we explicitly do NOT return the subprocess status code, as this 
    utility is meant to be used extensively in IPython, where any return value 
    would trigger :func:`sys.displayhook` calls. 
    """ 
    # The controller provides interactivity with both 
    # stdin and stdout 
    #import _process_win32_controller 
    #_process_win32_controller.system(cmd) 
 
    with AvoidUNCPath() as path: 
        if path is not None: 
            cmd = '"pushd %s &&"%s' % (path, cmd) 
        return process_handler(cmd, _system_body) 
 
def getoutput(cmd): 
    """Return standard output of executing cmd in a shell. 
 
    Accepts the same arguments as os.system(). 
 
    Parameters 
    ---------- 
    cmd : str or list 
      A command to be executed in the system shell. 
 
    Returns 
    ------- 
    stdout : str 
    """ 
 
    with AvoidUNCPath() as path: 
        if path is not None: 
            cmd = '"pushd %s &&"%s' % (path, cmd) 
        out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT) 
 
    if out is None: 
        out = b'' 
    return py3compat.bytes_to_str(out) 
 
try: 
    CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW 
    CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)] 
    CommandLineToArgvW.restype = POINTER(LPCWSTR) 
    LocalFree = ctypes.windll.kernel32.LocalFree 
    LocalFree.res_type = HLOCAL 
    LocalFree.arg_types = [HLOCAL] 
     
    def arg_split(commandline, posix=False, strict=True): 
        """Split a command line's arguments in a shell-like manner. 
 
        This is a special version for windows that use a ctypes call to CommandLineToArgvW 
        to do the argv splitting. The posix paramter is ignored. 
         
        If strict=False, process_common.arg_split(...strict=False) is used instead. 
        """ 
        #CommandLineToArgvW returns path to executable if called with empty string. 
        if commandline.strip() == "": 
            return [] 
        if not strict: 
            # not really a cl-arg, fallback on _process_common 
            return py_arg_split(commandline, posix=posix, strict=strict) 
        argvn = c_int() 
        result_pointer = CommandLineToArgvW(py3compat.cast_unicode(commandline.lstrip()), ctypes.byref(argvn)) 
        result_array_type = LPCWSTR * argvn.value 
        result = [arg for arg in result_array_type.from_address(ctypes.addressof(result_pointer.contents))] 
        retval = LocalFree(result_pointer) 
        return result 
except AttributeError: 
    arg_split = py_arg_split 
 
def check_pid(pid): 
    # OpenProcess returns 0 if no such process (of ours) exists 
    # positive int otherwise 
    return bool(ctypes.windll.kernel32.OpenProcess(1,0,pid))