/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/win32utils.py

Merge test-run support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
from __future__ import absolute_import
23
23
 
24
24
import glob
 
25
import operator
25
26
import os
26
27
import struct
27
28
import sys
31
32
    )
32
33
from breezy.i18n import gettext
33
34
 
34
 
has_ctypes_win32 = False
35
 
if sys.platform == 'win32':
36
 
    try:
37
 
        import ctypes
38
 
    except ImportError:
39
 
        has_ctypes_win32 = False
 
35
# We can cope without it; use a separate variable to help pyflakes
 
36
try:
 
37
    import ctypes
 
38
    has_ctypes = True
 
39
except ImportError:
 
40
    has_ctypes = False
 
41
else:
 
42
    create_buffer = ctypes.create_unicode_buffer
 
43
    extract_buffer = operator.attrgetter("value")
 
44
    suffix = 'W'
 
45
try:
 
46
    import pywintypes
 
47
    has_pywintypes = True
 
48
except ImportError:
 
49
    has_pywintypes = has_win32file = has_win32api = False
 
50
else:
 
51
    try:
 
52
        import win32file
 
53
        has_win32file = True
 
54
    except ImportError:
 
55
        has_win32file = False
 
56
    try:
 
57
        import win32api
 
58
        has_win32api = True
 
59
    except ImportError:
 
60
        has_win32api = False
40
61
 
 
62
# pulling in win32com.shell is a bit of overhead, and normally we don't need
 
63
# it as ctypes is preferred and common.  lazy_imports and "optional"
 
64
# modules don't work well, so we do our own lazy thing...
 
65
has_win32com_shell = None # Set to True or False once we know for sure...
41
66
 
42
67
# Special Win32 API constants
43
68
# Handles of std streams
47
72
 
48
73
# CSIDL constants (from MSDN 2003)
49
74
CSIDL_APPDATA = 0x001A      # Application Data folder
50
 
# <user name>\Local Settings\Application Data (non roaming)
51
 
CSIDL_LOCAL_APPDATA = 0x001c
 
75
CSIDL_LOCAL_APPDATA = 0x001c# <user name>\Local Settings\Application Data (non roaming)
52
76
CSIDL_PERSONAL = 0x0005     # My Documents folder
53
77
 
54
78
# from winapi C headers
64
88
def debug_memory_win32api(message='', short=True):
65
89
    """Use trace.note() to dump the running memory info."""
66
90
    from breezy import trace
67
 
    if has_ctypes_win32:
 
91
    if has_ctypes:
68
92
        class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure):
69
93
            """Used by GetProcessMemoryInfo"""
70
94
            _fields_ = [('cb', ctypes.c_ulong),
78
102
                        ('PagefileUsage', ctypes.c_size_t),
79
103
                        ('PeakPagefileUsage', ctypes.c_size_t),
80
104
                        ('PrivateUsage', ctypes.c_size_t),
81
 
                        ]
 
105
                       ]
82
106
        cur_process = ctypes.windll.kernel32.GetCurrentProcess()
83
107
        mem_struct = PROCESS_MEMORY_COUNTERS_EX()
84
 
        ret = ctypes.windll.psapi.GetProcessMemoryInfo(
85
 
            cur_process, ctypes.byref(mem_struct), ctypes.sizeof(mem_struct))
 
108
        ret = ctypes.windll.psapi.GetProcessMemoryInfo(cur_process,
 
109
            ctypes.byref(mem_struct),
 
110
            ctypes.sizeof(mem_struct))
86
111
        if not ret:
87
112
            trace.note(gettext('Failed to GetProcessMemoryInfo()'))
88
113
            return
97
122
                'PagefileUsage': mem_struct.PagefileUsage,
98
123
                'PeakPagefileUsage': mem_struct.PeakPagefileUsage,
99
124
                'PrivateUsage': mem_struct.PrivateUsage,
100
 
                }
 
125
               }
 
126
    elif has_win32api:
 
127
        import win32process
 
128
        # win32process does not return PrivateUsage, because it doesn't use
 
129
        # PROCESS_MEMORY_COUNTERS_EX (it uses the one without _EX).
 
130
        proc = win32process.GetCurrentProcess()
 
131
        info = win32process.GetProcessMemoryInfo(proc)
101
132
    else:
102
133
        trace.note(gettext('Cannot debug memory on win32 without ctypes'
103
 
                           ' or win32process'))
 
134
                   ' or win32process'))
104
135
        return
105
136
    if short:
106
137
        # using base-2 units (see HACKING.txt).
107
138
        trace.note(gettext('WorkingSize {0:>7}KiB'
108
 
                           '\tPeakWorking {1:>7}KiB\t{2}').format(
 
139
                   '\tPeakWorking {1:>7}KiB\t{2}').format(
109
140
                   info['WorkingSetSize'] / 1024,
110
141
                   info['PeakWorkingSetSize'] / 1024,
111
142
                   message))
112
143
        return
113
144
    if message:
114
145
        trace.note('%s', message)
115
 
    trace.note(gettext('WorkingSize       %8d KiB'),
116
 
               info['WorkingSetSize'] / 1024)
117
 
    trace.note(gettext('PeakWorking       %8d KiB'),
118
 
               info['PeakWorkingSetSize'] / 1024)
119
 
    trace.note(gettext('PagefileUsage     %8d KiB'),
120
 
               info.get('PagefileUsage', 0) / 1024)
 
146
    trace.note(gettext('WorkingSize       %8d KiB'), info['WorkingSetSize'] / 1024)
 
147
    trace.note(gettext('PeakWorking       %8d KiB'), info['PeakWorkingSetSize'] / 1024)
 
148
    trace.note(gettext('PagefileUsage     %8d KiB'), info.get('PagefileUsage', 0) / 1024)
121
149
    trace.note(gettext('PeakPagefileUsage %8d KiB'),
122
150
               info.get('PeakPagefileUsage', 0) / 1024)
123
 
    trace.note(gettext('PrivateUsage      %8d KiB'),
124
 
               info.get('PrivateUsage', 0) / 1024)
 
151
    trace.note(gettext('PrivateUsage      %8d KiB'), info.get('PrivateUsage', 0) / 1024)
125
152
    trace.note(gettext('PageFaultCount    %8d'), info.get('PageFaultCount', 0))
126
153
 
127
154
 
132
159
    console window and return tuple (sizex, sizey) if success,
133
160
    or default size (defaultx, defaulty) otherwise.
134
161
    """
135
 
    if not has_ctypes_win32:
 
162
    if not has_ctypes:
136
163
        # no ctypes is found
137
164
        return (defaultx, defaulty)
138
165
 
144
171
 
145
172
    if res:
146
173
        (bufx, bufy, curx, cury, wattr,
147
 
         left, top, right, bottom, maxx, maxy) = struct.unpack(
 
174
        left, top, right, bottom, maxx, maxy) = struct.unpack(
148
175
            "hhhhHhhhhhh", csbi.raw)
149
176
        sizex = right - left + 1
150
177
        sizey = bottom - top + 1
158
185
 
159
186
    Result is always unicode (or None).
160
187
    """
161
 
    if has_ctypes_win32:
 
188
    if has_ctypes:
162
189
        try:
163
190
            SHGetSpecialFolderPath = \
164
191
                ctypes.windll.shell32.SHGetSpecialFolderPathW
168
195
            buf = ctypes.create_unicode_buffer(MAX_PATH)
169
196
            if SHGetSpecialFolderPath(None, buf, csidl, 0):
170
197
                return buf.value
 
198
 
 
199
    global has_win32com_shell
 
200
    if has_win32com_shell is None:
 
201
        try:
 
202
            from win32com.shell import shell
 
203
            has_win32com_shell = True
 
204
        except ImportError:
 
205
            has_win32com_shell = False
 
206
    if has_win32com_shell:
 
207
        # still need to bind the name locally, but this is fast.
 
208
        from win32com.shell import shell
 
209
        try:
 
210
            return shell.SHGetSpecialFolderPath(0, csidl, 0)
 
211
        except shell.error:
 
212
            # possibly E_NOTIMPL meaning we can't load the function pointer,
 
213
            # or E_FAIL meaning the function failed - regardless, just ignore it
 
214
            pass
171
215
    return None
172
216
 
173
217
 
234
278
    """Return user name as login name.
235
279
    If name cannot be obtained return None.
236
280
    """
237
 
    if has_ctypes_win32:
 
281
    if has_ctypes:
238
282
        try:
239
283
            advapi32 = ctypes.windll.advapi32
240
 
            GetUserName = getattr(advapi32, 'GetUserNameW')
 
284
            GetUserName = getattr(advapi32, 'GetUserName'+suffix)
241
285
        except AttributeError:
242
286
            pass
243
287
        else:
244
 
            buf = ctypes.create_unicode_buffer(UNLEN + 1)
245
 
            n = ctypes.c_int(UNLEN + 1)
 
288
            buf = create_buffer(UNLEN+1)
 
289
            n = ctypes.c_int(UNLEN+1)
246
290
            if GetUserName(buf, ctypes.byref(n)):
247
 
                return buf.value
 
291
                return extract_buffer(buf)
248
292
    # otherwise try env variables
249
293
    return get_environ_unicode('USERNAME')
250
294
 
253
297
# computer or the cluster associated with the local computer."
254
298
_WIN32_ComputerNameDnsHostname = 1
255
299
 
256
 
 
257
300
def get_host_name():
258
301
    """Return host machine name.
259
302
    If name cannot be obtained return None.
260
303
 
261
304
    :return: A unicode string representing the host name.
262
305
    """
263
 
    if has_ctypes_win32:
 
306
    if has_win32api:
 
307
        try:
 
308
            return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
 
309
        except (NotImplementedError, win32api.error):
 
310
            # NotImplemented will happen on win9x...
 
311
            pass
 
312
    if has_ctypes:
264
313
        try:
265
314
            kernel32 = ctypes.windll.kernel32
266
315
        except AttributeError:
267
 
            pass  # Missing the module we need
 
316
            pass # Missing the module we need
268
317
        else:
269
 
            buf = ctypes.create_unicode_buffer(MAX_COMPUTERNAME_LENGTH + 1)
270
 
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH + 1)
 
318
            buf = create_buffer(MAX_COMPUTERNAME_LENGTH+1)
 
319
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
271
320
 
272
321
            # Try GetComputerNameEx which gives a proper Unicode hostname
273
 
            GetComputerNameEx = getattr(kernel32, 'GetComputerNameExW', None)
 
322
            GetComputerNameEx = getattr(kernel32, 'GetComputerNameEx'+suffix,
 
323
                                        None)
274
324
            if (GetComputerNameEx is not None
275
325
                and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
276
326
                                      buf, ctypes.byref(n))):
277
 
                return buf.value
 
327
                return extract_buffer(buf)
 
328
 
 
329
            # Try GetComputerName in case GetComputerNameEx wasn't found
 
330
            # It returns the NETBIOS name, which isn't as good, but still ok.
 
331
            # The first GetComputerNameEx might have changed 'n', so reset it
 
332
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
 
333
            GetComputerName = getattr(kernel32, 'GetComputerName'+suffix,
 
334
                                      None)
 
335
            if (GetComputerName is not None
 
336
                and GetComputerName(buf, ctypes.byref(n))):
 
337
                return extract_buffer(buf)
278
338
    return get_environ_unicode('COMPUTERNAME')
279
339
 
280
340
 
281
341
def _ensure_with_dir(path):
282
342
    if (not os.path.split(path)[0] or path.startswith(u'*')
283
 
            or path.startswith(u'?')):
 
343
        or path.startswith(u'?')):
284
344
        return u'./' + path, True
285
345
    else:
286
346
        return path, False
287
347
 
288
 
 
289
348
def _undo_ensure_with_dir(path, corrected):
290
349
    if corrected:
291
350
        return path[2:]
350
409
 
351
410
    try:
352
411
        hkey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
353
 
                               'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\' +
354
 
                               basename)
 
412
            'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\' +
 
413
            basename)
355
414
    except EnvironmentError:
356
415
        return appname
357
416
 
375
434
 
376
435
def set_file_attr_hidden(path):
377
436
    """Set file attributes to hidden if possible"""
378
 
    if not has_ctypes_win32:
379
 
        return
380
 
    from ctypes.wintypes import BOOL, DWORD, LPCWSTR
381
 
    _kernel32 = ctypes.windll.kernel32
382
 
    # <https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-setfileattributesw>
383
 
    _SetFileAttributesW = ctypes.WINFUNCTYPE(BOOL, LPCWSTR, DWORD)(
384
 
        ("SetFileAttributesW", _kernel32))
385
 
    FILE_ATTRIBUTE_HIDDEN = 2
386
 
    if not SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN):
387
 
        e = ctypes.WinError()
388
 
        from . import trace
389
 
        trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
 
437
    if has_win32file:
 
438
        SetFileAttributes = win32file.SetFileAttributesW
 
439
        try:
 
440
            SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)
 
441
        except pywintypes.error as e:
 
442
            from . import trace
 
443
            trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
390
444
 
391
445
 
392
446
def _command_line_to_argv(command_line, argv, single_quotes_allowed=False):
403
457
    :return: A list of unicode strings.
404
458
    """
405
459
    # First, split the command line
406
 
    s = cmdline.Splitter(
407
 
        command_line, single_quotes_allowed=single_quotes_allowed)
408
 
 
409
 
    # Bug #587868 Now make sure that the length of s agrees with sys.argv
410
 
    # we do this by simply counting the number of arguments in each. The counts should
411
 
    # agree no matter what encoding sys.argv is in (AFAIK)
412
 
    # len(arguments) < len(sys.argv) should be an impossibility since python gets
 
460
    s = cmdline.Splitter(command_line, single_quotes_allowed=single_quotes_allowed)
 
461
    
 
462
    # Bug #587868 Now make sure that the length of s agrees with sys.argv 
 
463
    # we do this by simply counting the number of arguments in each. The counts should 
 
464
    # agree no matter what encoding sys.argv is in (AFAIK) 
 
465
    # len(arguments) < len(sys.argv) should be an impossibility since python gets 
413
466
    # args from the very same PEB as does GetCommandLineW
414
467
    arguments = list(s)
415
 
 
 
468
    
416
469
    # Now shorten the command line we get from GetCommandLineW to match sys.argv
417
470
    if len(arguments) < len(argv):
418
471
        raise AssertionError("Split command line can't be shorter than argv")
419
472
    arguments = arguments[len(arguments) - len(argv):]
420
 
 
 
473
    
421
474
    # Carry on to process globs (metachars) in the command line
422
475
    # expand globs if necessary
423
476
    # TODO: Use 'globbing' instead of 'glob.glob', this gives us stuff like
431
484
    return args
432
485
 
433
486
 
434
 
if has_ctypes_win32:
 
487
if has_ctypes:
435
488
    def get_unicode_argv():
436
489
        prototype = ctypes.WINFUNCTYPE(ctypes.c_wchar_p)
437
490
        GetCommandLineW = prototype(("GetCommandLineW",
443
496
        argv = _command_line_to_argv(command_line, sys.argv)[1:]
444
497
        return argv
445
498
 
 
499
 
446
500
    def get_environ_unicode(key, default=None):
447
501
        """Get `key` from environment as unicode or `default` if unset
448
502
 
453
507
 
454
508
        A large enough buffer will be allocated to retrieve the value, though
455
509
        it may take two calls to the underlying library function.
 
510
 
 
511
        This needs ctypes because pywin32 does not expose the wide version.
456
512
        """
457
513
        cfunc = getattr(get_environ_unicode, "_c_function", None)
458
514
        if cfunc is None:
460
516
            cfunc = ctypes.WINFUNCTYPE(DWORD, LPCWSTR, LPWSTR, DWORD)(
461
517
                ("GetEnvironmentVariableW", ctypes.windll.kernel32))
462
518
            get_environ_unicode._c_function = cfunc
463
 
        buffer_size = 256  # heuristic, 256 characters often enough
 
519
        buffer_size = 256 # heuristic, 256 characters often enough
464
520
        while True:
465
 
            buf = ctypes.create_unicode_buffer(buffer_size)
466
 
            length = cfunc(key, buf, buffer_size)
 
521
            buffer = ctypes.create_unicode_buffer(buffer_size)
 
522
            length = cfunc(key, buffer, buffer_size)
467
523
            if not length:
468
524
                code = ctypes.GetLastError()
469
 
                if code == 203:  # ERROR_ENVVAR_NOT_FOUND
 
525
                if code == 203: # ERROR_ENVVAR_NOT_FOUND
470
526
                    return default
471
527
                raise ctypes.WinError(code)
472
528
            if buffer_size > length:
473
 
                return buf[:length]
 
529
                return buffer[:length]
474
530
            buffer_size = length
475
531
 
476
532
 
477
 
if has_ctypes_win32:
 
533
if has_win32api:
 
534
    def _pywin32_is_local_pid_dead(pid):
 
535
        """True if pid doesn't correspond to live process on this machine"""
 
536
        try:
 
537
            handle = win32api.OpenProcess(1, False, pid) # PROCESS_TERMINATE
 
538
        except pywintypes.error as e:
 
539
            if e[0] == 5: # ERROR_ACCESS_DENIED
 
540
                # Probably something alive we're not allowed to kill
 
541
                return False
 
542
            elif e[0] == 87: # ERROR_INVALID_PARAMETER
 
543
                return True
 
544
            raise
 
545
        handle.close()
 
546
        return False
 
547
    is_local_pid_dead = _pywin32_is_local_pid_dead
 
548
elif has_ctypes and sys.platform == 'win32':
478
549
    from ctypes.wintypes import BOOL, DWORD, HANDLE
479
550
    _kernel32 = ctypes.windll.kernel32
480
551
    _CloseHandle = ctypes.WINFUNCTYPE(BOOL, HANDLE)(
481
552
        ("CloseHandle", _kernel32))
482
553
    _OpenProcess = ctypes.WINFUNCTYPE(HANDLE, DWORD, BOOL, DWORD)(
483
554
        ("OpenProcess", _kernel32))
484
 
 
485
555
    def _ctypes_is_local_pid_dead(pid):
486
556
        """True if pid doesn't correspond to live process on this machine"""
487
 
        handle = _OpenProcess(1, False, pid)  # PROCESS_TERMINATE
 
557
        handle = _OpenProcess(1, False, pid) # PROCESS_TERMINATE
488
558
        if not handle:
489
559
            errorcode = ctypes.GetLastError()
490
 
            if errorcode == 5:  # ERROR_ACCESS_DENIED
 
560
            if errorcode == 5: # ERROR_ACCESS_DENIED
491
561
                # Probably something alive we're not allowed to kill
492
562
                return False
493
 
            elif errorcode == 87:  # ERROR_INVALID_PARAMETER
 
563
            elif errorcode == 87: # ERROR_INVALID_PARAMETER
494
564
                return True
495
565
            raise ctypes.WinError(errorcode)
496
566
        _CloseHandle(handle)
497
567
        return False
498
 
 
499
568
    is_local_pid_dead = _ctypes_is_local_pid_dead
 
569
 
 
570
 
 
571
def _is_pywintypes_error(evalue):
 
572
    """True if exception instance is an error from pywin32"""
 
573
    if has_pywintypes and isinstance(evalue, pywintypes.error):
 
574
        return True
 
575
    return False