##// END OF EJS Templates
fsmonitor: refresh pywatchman with upstream...
Gregory Szorc -
r43703:6469c23a stable
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (606 lines changed) Show them Hide them
@@ -26,10 +26,8 b''
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
29 # no unicode literals
30 from __future__ import absolute_import, division, print_function
33
31
34 import inspect
32 import inspect
35 import math
33 import math
@@ -38,33 +36,22 b' import socket'
38 import subprocess
36 import subprocess
39 import time
37 import time
40
38
39 from . import capabilities, compat, encoding, load
40
41
41 # Sometimes it's really hard to get Python extensions to compile,
42 # Sometimes it's really hard to get Python extensions to compile,
42 # so fall back to a pure Python implementation.
43 # so fall back to a pure Python implementation.
43 try:
44 try:
44 from . import bser
45 from . import bser
46
45 # Demandimport causes modules to be loaded lazily. Force the load now
47 # Demandimport causes modules to be loaded lazily. Force the load now
46 # so that we can fall back on pybser if bser doesn't exist
48 # so that we can fall back on pybser if bser doesn't exist
47 bser.pdu_info
49 bser.pdu_info
48 except ImportError:
50 except ImportError:
49 from . import pybser as bser
51 from . import pybser as bser
50
52
51 from mercurial.utils import (
52 procutil,
53 )
54
53
55 from mercurial import (
54 if os.name == "nt":
56 pycompat,
57 )
58
59 from . import (
60 capabilities,
61 compat,
62 encoding,
63 load,
64 )
65
66
67 if os.name == 'nt':
68 import ctypes
55 import ctypes
69 import ctypes.wintypes
56 import ctypes.wintypes
70
57
@@ -73,7 +60,7 b" if os.name == 'nt':"
73 GENERIC_WRITE = 0x40000000
60 GENERIC_WRITE = 0x40000000
74 FILE_FLAG_OVERLAPPED = 0x40000000
61 FILE_FLAG_OVERLAPPED = 0x40000000
75 OPEN_EXISTING = 3
62 OPEN_EXISTING = 3
76 INVALID_HANDLE_VALUE = -1
63 INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
77 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
64 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
78 FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
65 FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
79 FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
66 FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
@@ -92,9 +79,11 b" if os.name == 'nt':"
92
79
93 class OVERLAPPED(ctypes.Structure):
80 class OVERLAPPED(ctypes.Structure):
94 _fields_ = [
81 _fields_ = [
95 ("Internal", ULONG_PTR), ("InternalHigh", ULONG_PTR),
82 ("Internal", ULONG_PTR),
96 ("Offset", wintypes.DWORD), ("OffsetHigh", wintypes.DWORD),
83 ("InternalHigh", ULONG_PTR),
97 ("hEvent", wintypes.HANDLE)
84 ("Offset", wintypes.DWORD),
85 ("OffsetHigh", wintypes.DWORD),
86 ("hEvent", wintypes.HANDLE),
98 ]
87 ]
99
88
100 def __init__(self):
89 def __init__(self):
@@ -107,9 +96,15 b" if os.name == 'nt':"
107 LPDWORD = ctypes.POINTER(wintypes.DWORD)
96 LPDWORD = ctypes.POINTER(wintypes.DWORD)
108
97
109 CreateFile = ctypes.windll.kernel32.CreateFileA
98 CreateFile = ctypes.windll.kernel32.CreateFileA
110 CreateFile.argtypes = [wintypes.LPSTR, wintypes.DWORD, wintypes.DWORD,
99 CreateFile.argtypes = [
111 wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD,
100 wintypes.LPSTR,
112 wintypes.HANDLE]
101 wintypes.DWORD,
102 wintypes.DWORD,
103 wintypes.LPVOID,
104 wintypes.DWORD,
105 wintypes.DWORD,
106 wintypes.HANDLE,
107 ]
113 CreateFile.restype = wintypes.HANDLE
108 CreateFile.restype = wintypes.HANDLE
114
109
115 CloseHandle = ctypes.windll.kernel32.CloseHandle
110 CloseHandle = ctypes.windll.kernel32.CloseHandle
@@ -117,13 +112,23 b" if os.name == 'nt':"
117 CloseHandle.restype = wintypes.BOOL
112 CloseHandle.restype = wintypes.BOOL
118
113
119 ReadFile = ctypes.windll.kernel32.ReadFile
114 ReadFile = ctypes.windll.kernel32.ReadFile
120 ReadFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
115 ReadFile.argtypes = [
121 LPDWORD, ctypes.POINTER(OVERLAPPED)]
116 wintypes.HANDLE,
117 wintypes.LPVOID,
118 wintypes.DWORD,
119 LPDWORD,
120 ctypes.POINTER(OVERLAPPED),
121 ]
122 ReadFile.restype = wintypes.BOOL
122 ReadFile.restype = wintypes.BOOL
123
123
124 WriteFile = ctypes.windll.kernel32.WriteFile
124 WriteFile = ctypes.windll.kernel32.WriteFile
125 WriteFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
125 WriteFile.argtypes = [
126 LPDWORD, ctypes.POINTER(OVERLAPPED)]
126 wintypes.HANDLE,
127 wintypes.LPVOID,
128 wintypes.DWORD,
129 LPDWORD,
130 ctypes.POINTER(OVERLAPPED),
131 ]
127 WriteFile.restype = wintypes.BOOL
132 WriteFile.restype = wintypes.BOOL
128
133
129 GetLastError = ctypes.windll.kernel32.GetLastError
134 GetLastError = ctypes.windll.kernel32.GetLastError
@@ -135,34 +140,56 b" if os.name == 'nt':"
135 SetLastError.restype = None
140 SetLastError.restype = None
136
141
137 FormatMessage = ctypes.windll.kernel32.FormatMessageA
142 FormatMessage = ctypes.windll.kernel32.FormatMessageA
138 FormatMessage.argtypes = [wintypes.DWORD, wintypes.LPVOID, wintypes.DWORD,
143 FormatMessage.argtypes = [
139 wintypes.DWORD, ctypes.POINTER(wintypes.LPSTR),
144 wintypes.DWORD,
140 wintypes.DWORD, wintypes.LPVOID]
145 wintypes.LPVOID,
146 wintypes.DWORD,
147 wintypes.DWORD,
148 ctypes.POINTER(wintypes.LPSTR),
149 wintypes.DWORD,
150 wintypes.LPVOID,
151 ]
141 FormatMessage.restype = wintypes.DWORD
152 FormatMessage.restype = wintypes.DWORD
142
153
143 LocalFree = ctypes.windll.kernel32.LocalFree
154 LocalFree = ctypes.windll.kernel32.LocalFree
144
155
145 GetOverlappedResult = ctypes.windll.kernel32.GetOverlappedResult
156 GetOverlappedResult = ctypes.windll.kernel32.GetOverlappedResult
146 GetOverlappedResult.argtypes = [wintypes.HANDLE,
157 GetOverlappedResult.argtypes = [
147 ctypes.POINTER(OVERLAPPED), LPDWORD,
158 wintypes.HANDLE,
148 wintypes.BOOL]
159 ctypes.POINTER(OVERLAPPED),
160 LPDWORD,
161 wintypes.BOOL,
162 ]
149 GetOverlappedResult.restype = wintypes.BOOL
163 GetOverlappedResult.restype = wintypes.BOOL
150
164
151 GetOverlappedResultEx = getattr(ctypes.windll.kernel32,
165 GetOverlappedResultEx = getattr(
152 'GetOverlappedResultEx', None)
166 ctypes.windll.kernel32, "GetOverlappedResultEx", None
167 )
153 if GetOverlappedResultEx is not None:
168 if GetOverlappedResultEx is not None:
154 GetOverlappedResultEx.argtypes = [wintypes.HANDLE,
169 GetOverlappedResultEx.argtypes = [
155 ctypes.POINTER(OVERLAPPED), LPDWORD,
170 wintypes.HANDLE,
156 wintypes.DWORD, wintypes.BOOL]
171 ctypes.POINTER(OVERLAPPED),
172 LPDWORD,
173 wintypes.DWORD,
174 wintypes.BOOL,
175 ]
157 GetOverlappedResultEx.restype = wintypes.BOOL
176 GetOverlappedResultEx.restype = wintypes.BOOL
158
177
159 WaitForSingleObjectEx = ctypes.windll.kernel32.WaitForSingleObjectEx
178 WaitForSingleObjectEx = ctypes.windll.kernel32.WaitForSingleObjectEx
160 WaitForSingleObjectEx.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.BOOL]
179 WaitForSingleObjectEx.argtypes = [
180 wintypes.HANDLE,
181 wintypes.DWORD,
182 wintypes.BOOL,
183 ]
161 WaitForSingleObjectEx.restype = wintypes.DWORD
184 WaitForSingleObjectEx.restype = wintypes.DWORD
162
185
163 CreateEvent = ctypes.windll.kernel32.CreateEventA
186 CreateEvent = ctypes.windll.kernel32.CreateEventA
164 CreateEvent.argtypes = [LPDWORD, wintypes.BOOL, wintypes.BOOL,
187 CreateEvent.argtypes = [
165 wintypes.LPSTR]
188 LPDWORD,
189 wintypes.BOOL,
190 wintypes.BOOL,
191 wintypes.LPSTR,
192 ]
166 CreateEvent.restype = wintypes.HANDLE
193 CreateEvent.restype = wintypes.HANDLE
167
194
168 # Windows Vista is the minimum supported client for CancelIoEx.
195 # Windows Vista is the minimum supported client for CancelIoEx.
@@ -178,9 +205,15 b' sniff_len = 13'
178 if _debugging:
205 if _debugging:
179
206
180 def log(fmt, *args):
207 def log(fmt, *args):
181 print('[%s] %s' %
208 print(
182 (time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime()),
209 "[%s] %s"
183 fmt % args[:]))
210 % (
211 time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime()),
212 fmt % args[:],
213 )
214 )
215
216
184 else:
217 else:
185
218
186 def log(fmt, *args):
219 def log(fmt, *args):
@@ -193,8 +226,16 b' def _win32_strerror(err):'
193 # FormatMessage will allocate memory and assign it here
226 # FormatMessage will allocate memory and assign it here
194 buf = ctypes.c_char_p()
227 buf = ctypes.c_char_p()
195 FormatMessage(
228 FormatMessage(
196 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
229 FORMAT_MESSAGE_FROM_SYSTEM
197 | FORMAT_MESSAGE_IGNORE_INSERTS, None, err, 0, buf, 0, None)
230 | FORMAT_MESSAGE_ALLOCATE_BUFFER
231 | FORMAT_MESSAGE_IGNORE_INSERTS,
232 None,
233 err,
234 0,
235 buf,
236 0,
237 None,
238 )
198 try:
239 try:
199 return buf.value
240 return buf.value
200 finally:
241 finally:
@@ -211,21 +252,30 b' class WatchmanError(Exception):'
211
252
212 def __str__(self):
253 def __str__(self):
213 if self.cmd:
254 if self.cmd:
214 return '%s, while executing %s' % (self.msg, self.cmd)
255 return "%s, while executing %s" % (self.msg, self.cmd)
215 return self.msg
256 return self.msg
216
257
217
258
259 class BSERv1Unsupported(WatchmanError):
260 pass
261
262
263 class UseAfterFork(WatchmanError):
264 pass
265
266
218 class WatchmanEnvironmentError(WatchmanError):
267 class WatchmanEnvironmentError(WatchmanError):
219 def __init__(self, msg, errno, errmsg, cmd=None):
268 def __init__(self, msg, errno, errmsg, cmd=None):
220 super(WatchmanEnvironmentError, self).__init__(
269 super(WatchmanEnvironmentError, self).__init__(
221 '{0}: errno={1} errmsg={2}'.format(msg, errno, errmsg),
270 "{0}: errno={1} errmsg={2}".format(msg, errno, errmsg), cmd
222 cmd)
271 )
223
272
224
273
225 class SocketConnectError(WatchmanError):
274 class SocketConnectError(WatchmanError):
226 def __init__(self, sockpath, exc):
275 def __init__(self, sockpath, exc):
227 super(SocketConnectError, self).__init__(
276 super(SocketConnectError, self).__init__(
228 'unable to connect to %s: %s' % (sockpath, exc))
277 "unable to connect to %s: %s" % (sockpath, exc)
278 )
229 self.sockpath = sockpath
279 self.sockpath = sockpath
230 self.exc = exc
280 self.exc = exc
231
281
@@ -245,15 +295,16 b' class CommandError(WatchmanError):'
245
295
246 self.msg is the message returned by watchman.
296 self.msg is the message returned by watchman.
247 """
297 """
298
248 def __init__(self, msg, cmd=None):
299 def __init__(self, msg, cmd=None):
249 super(CommandError, self).__init__(
300 super(CommandError, self).__init__(
250 'watchman command error: %s' % (msg, ),
301 "watchman command error: %s" % (msg,), cmd
251 cmd,
252 )
302 )
253
303
254
304
255 class Transport(object):
305 class Transport(object):
256 """ communication transport to the watchman server """
306 """ communication transport to the watchman server """
307
257 buf = None
308 buf = None
258
309
259 def close(self):
310 def close(self):
@@ -289,7 +340,7 b' class Transport(object):'
289 while True:
340 while True:
290 b = self.readBytes(4096)
341 b = self.readBytes(4096)
291 if b"\n" in b:
342 if b"\n" in b:
292 result = b''.join(self.buf)
343 result = b"".join(self.buf)
293 (line, b) = b.split(b"\n", 1)
344 (line, b) = b.split(b"\n", 1)
294 self.buf = [b]
345 self.buf = [b]
295 return result + line
346 return result + line
@@ -298,6 +349,7 b' class Transport(object):'
298
349
299 class Codec(object):
350 class Codec(object):
300 """ communication encoding for the watchman server """
351 """ communication encoding for the watchman server """
352
301 transport = None
353 transport = None
302
354
303 def __init__(self, transport):
355 def __init__(self, transport):
@@ -315,9 +367,10 b' class Codec(object):'
315
367
316 class UnixSocketTransport(Transport):
368 class UnixSocketTransport(Transport):
317 """ local unix domain socket transport """
369 """ local unix domain socket transport """
370
318 sock = None
371 sock = None
319
372
320 def __init__(self, sockpath, timeout, watchman_exe):
373 def __init__(self, sockpath, timeout):
321 self.sockpath = sockpath
374 self.sockpath = sockpath
322 self.timeout = timeout
375 self.timeout = timeout
323
376
@@ -331,8 +384,9 b' class UnixSocketTransport(Transport):'
331 raise SocketConnectError(self.sockpath, e)
384 raise SocketConnectError(self.sockpath, e)
332
385
333 def close(self):
386 def close(self):
334 self.sock.close()
387 if self.sock:
335 self.sock = None
388 self.sock.close()
389 self.sock = None
336
390
337 def setTimeout(self, value):
391 def setTimeout(self, value):
338 self.timeout = value
392 self.timeout = value
@@ -342,16 +396,16 b' class UnixSocketTransport(Transport):'
342 try:
396 try:
343 buf = [self.sock.recv(size)]
397 buf = [self.sock.recv(size)]
344 if not buf[0]:
398 if not buf[0]:
345 raise WatchmanError('empty watchman response')
399 raise WatchmanError("empty watchman response")
346 return buf[0]
400 return buf[0]
347 except socket.timeout:
401 except socket.timeout:
348 raise SocketTimeout('timed out waiting for response')
402 raise SocketTimeout("timed out waiting for response")
349
403
350 def write(self, data):
404 def write(self, data):
351 try:
405 try:
352 self.sock.sendall(data)
406 self.sock.sendall(data)
353 except socket.timeout:
407 except socket.timeout:
354 raise SocketTimeout('timed out sending query command')
408 raise SocketTimeout("timed out sending query command")
355
409
356
410
357 def _get_overlapped_result_ex_impl(pipe, olap, nbytes, millis, alertable):
411 def _get_overlapped_result_ex_impl(pipe, olap, nbytes, millis, alertable):
@@ -364,7 +418,7 b' def _get_overlapped_result_ex_impl(pipe,'
364 source code (see get_overlapped_result_ex_impl in stream_win.c). This
418 source code (see get_overlapped_result_ex_impl in stream_win.c). This
365 way, maintenance should be simplified.
419 way, maintenance should be simplified.
366 """
420 """
367 log('Preparing to wait for maximum %dms', millis )
421 log("Preparing to wait for maximum %dms", millis)
368 if millis != 0:
422 if millis != 0:
369 waitReturnCode = WaitForSingleObjectEx(olap.hEvent, millis, alertable)
423 waitReturnCode = WaitForSingleObjectEx(olap.hEvent, millis, alertable)
370 if waitReturnCode == WAIT_OBJECT_0:
424 if waitReturnCode == WAIT_OBJECT_0:
@@ -383,12 +437,12 b' def _get_overlapped_result_ex_impl(pipe,'
383 elif waitReturnCode == WAIT_FAILED:
437 elif waitReturnCode == WAIT_FAILED:
384 # something went wrong calling WaitForSingleObjectEx
438 # something went wrong calling WaitForSingleObjectEx
385 err = GetLastError()
439 err = GetLastError()
386 log('WaitForSingleObjectEx failed: %s', _win32_strerror(err))
440 log("WaitForSingleObjectEx failed: %s", _win32_strerror(err))
387 return False
441 return False
388 else:
442 else:
389 # unexpected situation deserving investigation.
443 # unexpected situation deserving investigation.
390 err = GetLastError()
444 err = GetLastError()
391 log('Unexpected error: %s', _win32_strerror(err))
445 log("Unexpected error: %s", _win32_strerror(err))
392 return False
446 return False
393
447
394 return GetOverlappedResult(pipe, olap, nbytes, False)
448 return GetOverlappedResult(pipe, olap, nbytes, False)
@@ -397,36 +451,52 b' def _get_overlapped_result_ex_impl(pipe,'
397 class WindowsNamedPipeTransport(Transport):
451 class WindowsNamedPipeTransport(Transport):
398 """ connect to a named pipe """
452 """ connect to a named pipe """
399
453
400 def __init__(self, sockpath, timeout, watchman_exe):
454 def __init__(self, sockpath, timeout):
401 self.sockpath = sockpath
455 self.sockpath = sockpath
402 self.timeout = int(math.ceil(timeout * 1000))
456 self.timeout = int(math.ceil(timeout * 1000))
403 self._iobuf = None
457 self._iobuf = None
404
458
405 self.pipe = CreateFile(sockpath, GENERIC_READ | GENERIC_WRITE, 0, None,
459 if compat.PYTHON3:
406 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, None)
460 sockpath = os.fsencode(sockpath)
461 self.pipe = CreateFile(
462 sockpath,
463 GENERIC_READ | GENERIC_WRITE,
464 0,
465 None,
466 OPEN_EXISTING,
467 FILE_FLAG_OVERLAPPED,
468 None,
469 )
407
470
408 if self.pipe == INVALID_HANDLE_VALUE:
471 err = GetLastError()
472 if self.pipe == INVALID_HANDLE_VALUE or self.pipe == 0:
409 self.pipe = None
473 self.pipe = None
410 self._raise_win_err('failed to open pipe %s' % sockpath,
474 raise SocketConnectError(self.sockpath, self._make_win_err("", err))
411 GetLastError())
412
475
413 # event for the overlapped I/O operations
476 # event for the overlapped I/O operations
414 self._waitable = CreateEvent(None, True, False, None)
477 self._waitable = CreateEvent(None, True, False, None)
478 err = GetLastError()
415 if self._waitable is None:
479 if self._waitable is None:
416 self._raise_win_err('CreateEvent failed', GetLastError())
480 self._raise_win_err("CreateEvent failed", err)
417
481
418 self._get_overlapped_result_ex = GetOverlappedResultEx
482 self._get_overlapped_result_ex = GetOverlappedResultEx
419 if (os.getenv('WATCHMAN_WIN7_COMPAT') == '1' or
483 if (
420 self._get_overlapped_result_ex is None):
484 os.getenv("WATCHMAN_WIN7_COMPAT") == "1"
485 or self._get_overlapped_result_ex is None
486 ):
421 self._get_overlapped_result_ex = _get_overlapped_result_ex_impl
487 self._get_overlapped_result_ex = _get_overlapped_result_ex_impl
422
488
423 def _raise_win_err(self, msg, err):
489 def _raise_win_err(self, msg, err):
424 raise IOError('%s win32 error code: %d %s' %
490 raise self._make_win_err(msg, err)
425 (msg, err, _win32_strerror(err)))
491
492 def _make_win_err(self, msg, err):
493 return IOError(
494 "%s win32 error code: %d %s" % (msg, err, _win32_strerror(err))
495 )
426
496
427 def close(self):
497 def close(self):
428 if self.pipe:
498 if self.pipe:
429 log('Closing pipe')
499 log("Closing pipe")
430 CloseHandle(self.pipe)
500 CloseHandle(self.pipe)
431 self.pipe = None
501 self.pipe = None
432
502
@@ -460,7 +530,7 b' class WindowsNamedPipeTransport(Transpor'
460 olap = OVERLAPPED()
530 olap = OVERLAPPED()
461 olap.hEvent = self._waitable
531 olap.hEvent = self._waitable
462
532
463 log('made read buff of size %d', size)
533 log("made read buff of size %d", size)
464
534
465 # ReadFile docs warn against sending in the nread parameter for async
535 # ReadFile docs warn against sending in the nread parameter for async
466 # operations, so we always collect it via GetOverlappedResultEx
536 # operations, so we always collect it via GetOverlappedResultEx
@@ -469,23 +539,23 b' class WindowsNamedPipeTransport(Transpor'
469 if not immediate:
539 if not immediate:
470 err = GetLastError()
540 err = GetLastError()
471 if err != ERROR_IO_PENDING:
541 if err != ERROR_IO_PENDING:
472 self._raise_win_err('failed to read %d bytes' % size,
542 self._raise_win_err("failed to read %d bytes" % size, err)
473 GetLastError())
474
543
475 nread = wintypes.DWORD()
544 nread = wintypes.DWORD()
476 if not self._get_overlapped_result_ex(self.pipe, olap, nread,
545 if not self._get_overlapped_result_ex(
477 0 if immediate else self.timeout,
546 self.pipe, olap, nread, 0 if immediate else self.timeout, True
478 True):
547 ):
479 err = GetLastError()
548 err = GetLastError()
480 CancelIoEx(self.pipe, olap)
549 CancelIoEx(self.pipe, olap)
481
550
482 if err == WAIT_TIMEOUT:
551 if err == WAIT_TIMEOUT:
483 log('GetOverlappedResultEx timedout')
552 log("GetOverlappedResultEx timedout")
484 raise SocketTimeout('timed out after waiting %dms for read' %
553 raise SocketTimeout(
485 self.timeout)
554 "timed out after waiting %dms for read" % self.timeout
555 )
486
556
487 log('GetOverlappedResultEx reports error %d', err)
557 log("GetOverlappedResultEx reports error %d", err)
488 self._raise_win_err('error while waiting for read', err)
558 self._raise_win_err("error while waiting for read", err)
489
559
490 nread = nread.value
560 nread = nread.value
491 if nread == 0:
561 if nread == 0:
@@ -494,7 +564,7 b' class WindowsNamedPipeTransport(Transpor'
494 # other way this shows up is if the client has gotten in a weird
564 # other way this shows up is if the client has gotten in a weird
495 # state, so let's bail out
565 # state, so let's bail out
496 CancelIoEx(self.pipe, olap)
566 CancelIoEx(self.pipe, olap)
497 raise IOError('Async read yielded 0 bytes; unpossible!')
567 raise IOError("Async read yielded 0 bytes; unpossible!")
498
568
499 # Holds precisely the bytes that we read from the prior request
569 # Holds precisely the bytes that we read from the prior request
500 buf = buf[:nread]
570 buf = buf[:nread]
@@ -511,21 +581,25 b' class WindowsNamedPipeTransport(Transpor'
511 olap = OVERLAPPED()
581 olap = OVERLAPPED()
512 olap.hEvent = self._waitable
582 olap.hEvent = self._waitable
513
583
514 immediate = WriteFile(self.pipe, ctypes.c_char_p(data), len(data),
584 immediate = WriteFile(
515 None, olap)
585 self.pipe, ctypes.c_char_p(data), len(data), None, olap
586 )
516
587
517 if not immediate:
588 if not immediate:
518 err = GetLastError()
589 err = GetLastError()
519 if err != ERROR_IO_PENDING:
590 if err != ERROR_IO_PENDING:
520 self._raise_win_err('failed to write %d bytes' % len(data),
591 self._raise_win_err(
521 GetLastError())
592 "failed to write %d bytes to handle %r"
593 % (len(data), self.pipe),
594 err,
595 )
522
596
523 # Obtain results, waiting if needed
597 # Obtain results, waiting if needed
524 nwrote = wintypes.DWORD()
598 nwrote = wintypes.DWORD()
525 if self._get_overlapped_result_ex(self.pipe, olap, nwrote,
599 if self._get_overlapped_result_ex(
526 0 if immediate else self.timeout,
600 self.pipe, olap, nwrote, 0 if immediate else self.timeout, True
527 True):
601 ):
528 log('made write of %d bytes', nwrote.value)
602 log("made write of %d bytes", nwrote.value)
529 return nwrote.value
603 return nwrote.value
530
604
531 err = GetLastError()
605 err = GetLastError()
@@ -535,10 +609,21 b' class WindowsNamedPipeTransport(Transpor'
535 CancelIoEx(self.pipe, olap)
609 CancelIoEx(self.pipe, olap)
536
610
537 if err == WAIT_TIMEOUT:
611 if err == WAIT_TIMEOUT:
538 raise SocketTimeout('timed out after waiting %dms for write' %
612 raise SocketTimeout(
539 self.timeout)
613 "timed out after waiting %dms for write" % self.timeout
540 self._raise_win_err('error while waiting for write of %d bytes' %
614 )
541 len(data), err)
615 self._raise_win_err(
616 "error while waiting for write of %d bytes" % len(data), err
617 )
618
619
620 def _default_binpath(binpath=None):
621 if binpath:
622 return binpath
623 # The test harness sets WATCHMAN_BINARY to the binary under test,
624 # so we use that by default, otherwise, allow resolving watchman
625 # from the users PATH.
626 return os.environ.get("WATCHMAN_BINARY", "watchman")
542
627
543
628
544 class CLIProcessTransport(Transport):
629 class CLIProcessTransport(Transport):
@@ -560,13 +645,14 b' class CLIProcessTransport(Transport):'
560 It is the responsibility of the caller to set the send and
645 It is the responsibility of the caller to set the send and
561 receive codecs appropriately.
646 receive codecs appropriately.
562 """
647 """
648
563 proc = None
649 proc = None
564 closed = True
650 closed = True
565
651
566 def __init__(self, sockpath, timeout, watchman_exe):
652 def __init__(self, sockpath, timeout, binpath=None):
567 self.sockpath = sockpath
653 self.sockpath = sockpath
568 self.timeout = timeout
654 self.timeout = timeout
569 self.watchman_exe = watchman_exe
655 self.binpath = _default_binpath(binpath)
570
656
571 def close(self):
657 def close(self):
572 if self.proc:
658 if self.proc:
@@ -574,32 +660,32 b' class CLIProcessTransport(Transport):'
574 self.proc.kill()
660 self.proc.kill()
575 self.proc.stdin.close()
661 self.proc.stdin.close()
576 self.proc.stdout.close()
662 self.proc.stdout.close()
663 self.proc.wait()
577 self.proc = None
664 self.proc = None
578
665
579 def _connect(self):
666 def _connect(self):
580 if self.proc:
667 if self.proc:
581 return self.proc
668 return self.proc
582 args = [
669 args = [
583 self.watchman_exe,
670 self.binpath,
584 '--sockname={0}'.format(self.sockpath),
671 "--sockname={0}".format(self.sockpath),
585 '--logfile=/BOGUS',
672 "--logfile=/BOGUS",
586 '--statefile=/BOGUS',
673 "--statefile=/BOGUS",
587 '--no-spawn',
674 "--no-spawn",
588 '--no-local',
675 "--no-local",
589 '--no-pretty',
676 "--no-pretty",
590 '-j',
677 "-j",
591 ]
678 ]
592 self.proc = subprocess.Popen(pycompat.rapply(procutil.tonativestr,
679 self.proc = subprocess.Popen(
593 args),
680 args, stdin=subprocess.PIPE, stdout=subprocess.PIPE
594 stdin=subprocess.PIPE,
681 )
595 stdout=subprocess.PIPE)
596 return self.proc
682 return self.proc
597
683
598 def readBytes(self, size):
684 def readBytes(self, size):
599 self._connect()
685 self._connect()
600 res = self.proc.stdout.read(size)
686 res = self.proc.stdout.read(size)
601 if res == '':
687 if not res:
602 raise WatchmanError('EOF on CLI process transport')
688 raise WatchmanError("EOF on CLI process transport")
603 return res
689 return res
604
690
605 def write(self, data):
691 def write(self, data):
@@ -616,13 +702,22 b' class CLIProcessTransport(Transport):'
616 class BserCodec(Codec):
702 class BserCodec(Codec):
617 """ use the BSER encoding. This is the default, preferred codec """
703 """ use the BSER encoding. This is the default, preferred codec """
618
704
705 def __init__(self, transport, value_encoding, value_errors):
706 super(BserCodec, self).__init__(transport)
707 self._value_encoding = value_encoding
708 self._value_errors = value_errors
709
619 def _loads(self, response):
710 def _loads(self, response):
620 return bser.loads(response) # Defaults to BSER v1
711 return bser.loads(
712 response,
713 value_encoding=self._value_encoding,
714 value_errors=self._value_errors,
715 )
621
716
622 def receive(self):
717 def receive(self):
623 buf = [self.transport.readBytes(sniff_len)]
718 buf = [self.transport.readBytes(sniff_len)]
624 if not buf[0]:
719 if not buf[0]:
625 raise WatchmanError('empty watchman response')
720 raise WatchmanError("empty watchman response")
626
721
627 _1, _2, elen = bser.pdu_info(buf[0])
722 _1, _2, elen = bser.pdu_info(buf[0])
628
723
@@ -631,15 +726,15 b' class BserCodec(Codec):'
631 buf.append(self.transport.readBytes(elen - rlen))
726 buf.append(self.transport.readBytes(elen - rlen))
632 rlen += len(buf[-1])
727 rlen += len(buf[-1])
633
728
634 response = b''.join(buf)
729 response = b"".join(buf)
635 try:
730 try:
636 res = self._loads(response)
731 res = self._loads(response)
637 return res
732 return res
638 except ValueError as e:
733 except ValueError as e:
639 raise WatchmanError('watchman response decode error: %s' % e)
734 raise WatchmanError("watchman response decode error: %s" % e)
640
735
641 def send(self, *args):
736 def send(self, *args):
642 cmd = bser.dumps(*args) # Defaults to BSER v1
737 cmd = bser.dumps(*args) # Defaults to BSER v1
643 self.transport.write(cmd)
738 self.transport.write(cmd)
644
739
645
740
@@ -648,74 +743,96 b' class ImmutableBserCodec(BserCodec):'
648 immutable object support """
743 immutable object support """
649
744
650 def _loads(self, response):
745 def _loads(self, response):
651 return bser.loads(response, False) # Defaults to BSER v1
746 return bser.loads(
747 response,
748 False,
749 value_encoding=self._value_encoding,
750 value_errors=self._value_errors,
751 )
652
752
653
753
654 class Bser2WithFallbackCodec(BserCodec):
754 class Bser2WithFallbackCodec(BserCodec):
655 """ use BSER v2 encoding """
755 """ use BSER v2 encoding """
656
756
657 def __init__(self, transport):
757 def __init__(self, transport, value_encoding, value_errors):
658 super(Bser2WithFallbackCodec, self).__init__(transport)
758 super(Bser2WithFallbackCodec, self).__init__(
659 # Once the server advertises support for bser-v2 we should switch this
759 transport, value_encoding, value_errors
660 # to 'required' on Python 3.
760 )
661 self.send(["version", {"optional": ["bser-v2"]}])
761 if compat.PYTHON3:
762 bserv2_key = "required"
763 else:
764 bserv2_key = "optional"
765
766 self.send(["version", {bserv2_key: ["bser-v2"]}])
662
767
663 capabilities = self.receive()
768 capabilities = self.receive()
664
769
665 if 'error' in capabilities:
770 if "error" in capabilities:
666 raise Exception('Unsupported BSER version')
771 raise BSERv1Unsupported(
772 "The watchman server version does not support Python 3. Please "
773 "upgrade your watchman server."
774 )
667
775
668 if capabilities['capabilities']['bser-v2']:
776 if capabilities["capabilities"]["bser-v2"]:
669 self.bser_version = 2
777 self.bser_version = 2
670 self.bser_capabilities = 0
778 self.bser_capabilities = 0
671 else:
779 else:
672 self.bser_version = 1
780 self.bser_version = 1
673 self.bser_capabilities = 0
781 self.bser_capabilities = 0
674
782
675 def _loads(self, response):
676 return bser.loads(response)
677
678 def receive(self):
783 def receive(self):
679 buf = [self.transport.readBytes(sniff_len)]
784 buf = [self.transport.readBytes(sniff_len)]
680 if not buf[0]:
785 if not buf[0]:
681 raise WatchmanError('empty watchman response')
786 raise WatchmanError("empty watchman response")
682
787
683 recv_bser_version, recv_bser_capabilities, elen = bser.pdu_info(buf[0])
788 recv_bser_version, recv_bser_capabilities, elen = bser.pdu_info(buf[0])
684
789
685 if hasattr(self, 'bser_version'):
790 if hasattr(self, "bser_version"):
686 # Readjust BSER version and capabilities if necessary
791 # Readjust BSER version and capabilities if necessary
687 self.bser_version = max(self.bser_version, recv_bser_version)
792 self.bser_version = max(self.bser_version, recv_bser_version)
688 self.capabilities = self.bser_capabilities & recv_bser_capabilities
793 self.capabilities = self.bser_capabilities & recv_bser_capabilities
689
794
690 rlen = len(buf[0])
795 rlen = len(buf[0])
691 while elen > rlen:
796 while elen > rlen:
692 buf.append(self.transport.readBytes(elen - rlen))
797 buf.append(self.transport.readBytes(elen - rlen))
693 rlen += len(buf[-1])
798 rlen += len(buf[-1])
694
799
695 response = b''.join(buf)
800 response = b"".join(buf)
696 try:
801 try:
697 res = self._loads(response)
802 res = self._loads(response)
698 return res
803 return res
699 except ValueError as e:
804 except ValueError as e:
700 raise WatchmanError('watchman response decode error: %s' % e)
805 raise WatchmanError("watchman response decode error: %s" % e)
701
806
702 def send(self, *args):
807 def send(self, *args):
703 if hasattr(self, 'bser_version'):
808 if hasattr(self, "bser_version"):
704 cmd = bser.dumps(*args, version=self.bser_version,
809 cmd = bser.dumps(
705 capabilities=self.bser_capabilities)
810 *args,
811 version=self.bser_version,
812 capabilities=self.bser_capabilities
813 )
706 else:
814 else:
707 cmd = bser.dumps(*args)
815 cmd = bser.dumps(*args)
708 self.transport.write(cmd)
816 self.transport.write(cmd)
709
817
710
818
819 class ImmutableBser2Codec(Bser2WithFallbackCodec, ImmutableBserCodec):
820 """ use the BSER encoding, decoding values using the newer
821 immutable object support """
822
823 pass
824
825
711 class JsonCodec(Codec):
826 class JsonCodec(Codec):
712 """ Use json codec. This is here primarily for testing purposes """
827 """ Use json codec. This is here primarily for testing purposes """
828
713 json = None
829 json = None
714
830
715 def __init__(self, transport):
831 def __init__(self, transport):
716 super(JsonCodec, self).__init__(transport)
832 super(JsonCodec, self).__init__(transport)
717 # optional dep on json, only if JsonCodec is used
833 # optional dep on json, only if JsonCodec is used
718 import json
834 import json
835
719 self.json = json
836 self.json = json
720
837
721 def receive(self):
838 def receive(self):
@@ -727,7 +844,7 b' class JsonCodec(Codec):'
727 # but it's possible we might get non-ASCII bytes that are valid
844 # but it's possible we might get non-ASCII bytes that are valid
728 # UTF-8.
845 # UTF-8.
729 if compat.PYTHON3:
846 if compat.PYTHON3:
730 line = line.decode('utf-8')
847 line = line.decode("utf-8")
731 return self.json.loads(line)
848 return self.json.loads(line)
732 except Exception as e:
849 except Exception as e:
733 print(e, line)
850 print(e, line)
@@ -739,12 +856,13 b' class JsonCodec(Codec):'
739 # containing Unicode strings to Unicode string. Even with (the default)
856 # containing Unicode strings to Unicode string. Even with (the default)
740 # ensure_ascii=True, dumps returns a Unicode string.
857 # ensure_ascii=True, dumps returns a Unicode string.
741 if compat.PYTHON3:
858 if compat.PYTHON3:
742 cmd = cmd.encode('ascii')
859 cmd = cmd.encode("ascii")
743 self.transport.write(cmd + b"\n")
860 self.transport.write(cmd + b"\n")
744
861
745
862
746 class client(object):
863 class client(object):
747 """ Handles the communication with the watchman service """
864 """ Handles the communication with the watchman service """
865
748 sockpath = None
866 sockpath = None
749 transport = None
867 transport = None
750 sendCodec = None
868 sendCodec = None
@@ -754,60 +872,100 b' class client(object):'
754 subs = {} # Keyed by subscription name
872 subs = {} # Keyed by subscription name
755 sub_by_root = {} # Keyed by root, then by subscription name
873 sub_by_root = {} # Keyed by root, then by subscription name
756 logs = [] # When log level is raised
874 logs = [] # When log level is raised
757 unilateral = ['log', 'subscription']
875 unilateral = ["log", "subscription"]
758 tport = None
876 tport = None
759 useImmutableBser = None
877 useImmutableBser = None
760 watchman_exe = None
878 pid = None
761
879
762 def __init__(self,
880 def __init__(
763 sockpath=None,
881 self,
764 timeout=1.0,
882 sockpath=None,
765 transport=None,
883 timeout=1.0,
766 sendEncoding=None,
884 transport=None,
767 recvEncoding=None,
885 sendEncoding=None,
768 useImmutableBser=False,
886 recvEncoding=None,
769 watchman_exe=None):
887 useImmutableBser=False,
888 # use False for these two because None has a special
889 # meaning
890 valueEncoding=False,
891 valueErrors=False,
892 binpath=None,
893 ):
770 self.sockpath = sockpath
894 self.sockpath = sockpath
771 self.timeout = timeout
895 self.timeout = timeout
772 self.useImmutableBser = useImmutableBser
896 self.useImmutableBser = useImmutableBser
773 self.watchman_exe = watchman_exe
897 self.binpath = _default_binpath(binpath)
774
898
775 if inspect.isclass(transport) and issubclass(transport, Transport):
899 if inspect.isclass(transport) and issubclass(transport, Transport):
776 self.transport = transport
900 self.transport = transport
777 else:
901 else:
778 transport = transport or os.getenv('WATCHMAN_TRANSPORT') or 'local'
902 transport = transport or os.getenv("WATCHMAN_TRANSPORT") or "local"
779 if transport == 'local' and os.name == 'nt':
903 if transport == "local" and os.name == "nt":
780 self.transport = WindowsNamedPipeTransport
904 self.transport = WindowsNamedPipeTransport
781 elif transport == 'local':
905 elif transport == "local":
782 self.transport = UnixSocketTransport
906 self.transport = UnixSocketTransport
783 elif transport == 'cli':
907 elif transport == "cli":
784 self.transport = CLIProcessTransport
908 self.transport = CLIProcessTransport
785 if sendEncoding is None:
909 if sendEncoding is None:
786 sendEncoding = 'json'
910 sendEncoding = "json"
787 if recvEncoding is None:
911 if recvEncoding is None:
788 recvEncoding = sendEncoding
912 recvEncoding = sendEncoding
789 else:
913 else:
790 raise WatchmanError('invalid transport %s' % transport)
914 raise WatchmanError("invalid transport %s" % transport)
791
915
792 sendEncoding = str(sendEncoding or os.getenv('WATCHMAN_ENCODING') or
916 sendEncoding = str(
793 'bser')
917 sendEncoding or os.getenv("WATCHMAN_ENCODING") or "bser"
794 recvEncoding = str(recvEncoding or os.getenv('WATCHMAN_ENCODING') or
918 )
795 'bser')
919 recvEncoding = str(
920 recvEncoding or os.getenv("WATCHMAN_ENCODING") or "bser"
921 )
796
922
797 self.recvCodec = self._parseEncoding(recvEncoding)
923 self.recvCodec = self._parseEncoding(recvEncoding)
798 self.sendCodec = self._parseEncoding(sendEncoding)
924 self.sendCodec = self._parseEncoding(sendEncoding)
799
925
926 # We want to act like the native OS methods as much as possible. This
927 # means returning bytestrings on Python 2 by default and Unicode
928 # strings on Python 3. However we take an optional argument that lets
929 # users override this.
930 if valueEncoding is False:
931 if compat.PYTHON3:
932 self.valueEncoding = encoding.get_local_encoding()
933 self.valueErrors = encoding.default_local_errors
934 else:
935 self.valueEncoding = None
936 self.valueErrors = None
937 else:
938 self.valueEncoding = valueEncoding
939 if valueErrors is False:
940 self.valueErrors = encoding.default_local_errors
941 else:
942 self.valueErrors = valueErrors
943
944 def _makeBSERCodec(self, codec):
945 def make_codec(transport):
946 return codec(transport, self.valueEncoding, self.valueErrors)
947
948 return make_codec
949
800 def _parseEncoding(self, enc):
950 def _parseEncoding(self, enc):
801 if enc == 'bser':
951 if enc == "bser":
802 if self.useImmutableBser:
952 if self.useImmutableBser:
803 return ImmutableBserCodec
953 return self._makeBSERCodec(ImmutableBser2Codec)
804 return BserCodec
954 return self._makeBSERCodec(Bser2WithFallbackCodec)
805 elif enc == 'experimental-bser-v2':
955 elif enc == "bser-v1":
806 return Bser2WithFallbackCodec
956 if compat.PYTHON3:
807 elif enc == 'json':
957 raise BSERv1Unsupported(
958 "Python 3 does not support the BSER v1 encoding: specify "
959 '"bser" or omit the sendEncoding and recvEncoding '
960 "arguments"
961 )
962 if self.useImmutableBser:
963 return self._makeBSERCodec(ImmutableBserCodec)
964 return self._makeBSERCodec(BserCodec)
965 elif enc == "json":
808 return JsonCodec
966 return JsonCodec
809 else:
967 else:
810 raise WatchmanError('invalid encoding %s' % enc)
968 raise WatchmanError("invalid encoding %s" % enc)
811
969
812 def _hasprop(self, result, name):
970 def _hasprop(self, result, name):
813 if self.useImmutableBser:
971 if self.useImmutableBser:
@@ -817,29 +975,28 b' class client(object):'
817 def _resolvesockname(self):
975 def _resolvesockname(self):
818 # if invoked via a trigger, watchman will set this env var; we
976 # if invoked via a trigger, watchman will set this env var; we
819 # should use it unless explicitly set otherwise
977 # should use it unless explicitly set otherwise
820 path = os.getenv('WATCHMAN_SOCK')
978 path = os.getenv("WATCHMAN_SOCK")
821 if path:
979 if path:
822 return path
980 return path
823
981
824 cmd = [self.watchman_exe, '--output-encoding=bser', 'get-sockname']
982 cmd = [self.binpath, "--output-encoding=bser", "get-sockname"]
825 try:
983 try:
826 args = dict(stdout=subprocess.PIPE,
984 args = dict(
827 stderr=subprocess.PIPE,
985 stdout=subprocess.PIPE, stderr=subprocess.PIPE
828 close_fds=os.name != 'nt')
986 ) # noqa: C408
829
987
830 if os.name == 'nt':
988 if os.name == "nt":
831 # if invoked via an application with graphical user interface,
989 # if invoked via an application with graphical user interface,
832 # this call will cause a brief command window pop-up.
990 # this call will cause a brief command window pop-up.
833 # Using the flag STARTF_USESHOWWINDOW to avoid this behavior.
991 # Using the flag STARTF_USESHOWWINDOW to avoid this behavior.
834 startupinfo = subprocess.STARTUPINFO()
992 startupinfo = subprocess.STARTUPINFO()
835 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
993 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
836 args['startupinfo'] = startupinfo
994 args["startupinfo"] = startupinfo
837
995
838 p = subprocess.Popen(pycompat.rapply(procutil.tonativestr, cmd),
996 p = subprocess.Popen(cmd, **args)
839 **args)
840
997
841 except OSError as e:
998 except OSError as e:
842 raise WatchmanError('"watchman" executable not in PATH (%s)' % e)
999 raise WatchmanError('"watchman" executable not in PATH (%s)', e)
843
1000
844 stdout, stderr = p.communicate()
1001 stdout, stderr = p.communicate()
845 exitcode = p.poll()
1002 exitcode = p.poll()
@@ -848,27 +1005,43 b' class client(object):'
848 raise WatchmanError("watchman exited with code %d" % exitcode)
1005 raise WatchmanError("watchman exited with code %d" % exitcode)
849
1006
850 result = bser.loads(stdout)
1007 result = bser.loads(stdout)
851 if b'error' in result:
1008 if "error" in result:
852 raise WatchmanError('get-sockname error: %s' % result['error'])
1009 raise WatchmanError("get-sockname error: %s" % result["error"])
853
1010
854 return result[b'sockname']
1011 return result["sockname"]
855
1012
856 def _connect(self):
1013 def _connect(self):
857 """ establish transport connection """
1014 """ establish transport connection """
858
1015
859 if self.recvConn:
1016 if self.recvConn:
1017 if self.pid != os.getpid():
1018 raise UseAfterFork(
1019 "do not re-use a connection after fork; open a new client instead"
1020 )
860 return
1021 return
861
1022
862 if self.sockpath is None:
1023 if self.sockpath is None:
863 self.sockpath = self._resolvesockname()
1024 self.sockpath = self._resolvesockname()
864
1025
865 self.tport = self.transport(self.sockpath, self.timeout, self.watchman_exe)
1026 kwargs = {}
1027 if self.transport == CLIProcessTransport:
1028 kwargs["binpath"] = self.binpath
1029
1030 self.tport = self.transport(self.sockpath, self.timeout, **kwargs)
866 self.sendConn = self.sendCodec(self.tport)
1031 self.sendConn = self.sendCodec(self.tport)
867 self.recvConn = self.recvCodec(self.tport)
1032 self.recvConn = self.recvCodec(self.tport)
1033 self.pid = os.getpid()
868
1034
869 def __del__(self):
1035 def __del__(self):
870 self.close()
1036 self.close()
871
1037
1038 def __enter__(self):
1039 self._connect()
1040 return self
1041
1042 def __exit__(self, exc_type, exc_value, exc_traceback):
1043 self.close()
1044
872 def close(self):
1045 def close(self):
873 if self.tport:
1046 if self.tport:
874 self.tport.close()
1047 self.tport.close()
@@ -893,26 +1066,20 b' class client(object):'
893
1066
894 self._connect()
1067 self._connect()
895 result = self.recvConn.receive()
1068 result = self.recvConn.receive()
896 if self._hasprop(result, 'error'):
1069 if self._hasprop(result, "error"):
897 error = result['error']
1070 raise CommandError(result["error"])
898 if compat.PYTHON3 and isinstance(self.recvConn, BserCodec):
899 error = result['error'].decode('utf-8', 'surrogateescape')
900 raise CommandError(error)
901
1071
902 if self._hasprop(result, 'log'):
1072 if self._hasprop(result, "log"):
903 log = result['log']
1073 self.logs.append(result["log"])
904 if compat.PYTHON3 and isinstance(self.recvConn, BserCodec):
905 log = log.decode('utf-8', 'surrogateescape')
906 self.logs.append(log)
907
1074
908 if self._hasprop(result, 'subscription'):
1075 if self._hasprop(result, "subscription"):
909 sub = result['subscription']
1076 sub = result["subscription"]
910 if not (sub in self.subs):
1077 if not (sub in self.subs):
911 self.subs[sub] = []
1078 self.subs[sub] = []
912 self.subs[sub].append(result)
1079 self.subs[sub].append(result)
913
1080
914 # also accumulate in {root,sub} keyed store
1081 # also accumulate in {root,sub} keyed store
915 root = os.path.normcase(result['root'])
1082 root = os.path.normpath(os.path.normcase(result["root"]))
916 if not root in self.sub_by_root:
1083 if not root in self.sub_by_root:
917 self.sub_by_root[root] = {}
1084 self.sub_by_root[root] = {}
918 if not sub in self.sub_by_root[root]:
1085 if not sub in self.sub_by_root[root]:
@@ -922,7 +1089,7 b' class client(object):'
922 return result
1089 return result
923
1090
924 def isUnilateralResponse(self, res):
1091 def isUnilateralResponse(self, res):
925 if 'unilateral' in res and res['unilateral']:
1092 if "unilateral" in res and res["unilateral"]:
926 return True
1093 return True
927 # Fall back to checking for known unilateral responses
1094 # Fall back to checking for known unilateral responses
928 for k in self.unilateral:
1095 for k in self.unilateral:
@@ -955,18 +1122,11 b' class client(object):'
955 remove processing impacts both the unscoped and scoped stores
1122 remove processing impacts both the unscoped and scoped stores
956 for the subscription data.
1123 for the subscription data.
957 """
1124 """
958 if compat.PYTHON3 and issubclass(self.recvCodec, BserCodec):
959 # People may pass in Unicode strings here -- but currently BSER only
960 # returns bytestrings. Deal with that.
961 if isinstance(root, str):
962 root = encoding.encode_local(root)
963 if isinstance(name, str):
964 name = name.encode('utf-8')
965
966 if root is not None:
1125 if root is not None:
967 if not root in self.sub_by_root:
1126 root = os.path.normpath(os.path.normcase(root))
1127 if root not in self.sub_by_root:
968 return None
1128 return None
969 if not name in self.sub_by_root[root]:
1129 if name not in self.sub_by_root[root]:
970 return None
1130 return None
971 sub = self.sub_by_root[root][name]
1131 sub = self.sub_by_root[root][name]
972 if remove:
1132 if remove:
@@ -976,7 +1136,7 b' class client(object):'
976 del self.subs[name]
1136 del self.subs[name]
977 return sub
1137 return sub
978
1138
979 if not (name in self.subs):
1139 if name not in self.subs:
980 return None
1140 return None
981 sub = self.subs[name]
1141 sub = self.subs[name]
982 if remove:
1142 if remove:
@@ -992,7 +1152,7 b' class client(object):'
992 and NOT returned via this method.
1152 and NOT returned via this method.
993 """
1153 """
994
1154
995 log('calling client.query')
1155 log("calling client.query")
996 self._connect()
1156 self._connect()
997 try:
1157 try:
998 self.sendConn.send(args)
1158 self.sendConn.send(args)
@@ -1006,27 +1166,27 b' class client(object):'
1006 # When we can depend on Python 3, we can use PEP 3134
1166 # When we can depend on Python 3, we can use PEP 3134
1007 # exception chaining here.
1167 # exception chaining here.
1008 raise WatchmanEnvironmentError(
1168 raise WatchmanEnvironmentError(
1009 'I/O error communicating with watchman daemon',
1169 "I/O error communicating with watchman daemon",
1010 ee.errno,
1170 ee.errno,
1011 ee.strerror,
1171 ee.strerror,
1012 args)
1172 args,
1173 )
1013 except WatchmanError as ex:
1174 except WatchmanError as ex:
1014 ex.setCommand(args)
1175 ex.setCommand(args)
1015 raise
1176 raise
1016
1177
1017 def capabilityCheck(self, optional=None, required=None):
1178 def capabilityCheck(self, optional=None, required=None):
1018 """ Perform a server capability check """
1179 """ Perform a server capability check """
1019 res = self.query('version', {
1180 res = self.query(
1020 'optional': optional or [],
1181 "version", {"optional": optional or [], "required": required or []}
1021 'required': required or []
1182 )
1022 })
1023
1183
1024 if not self._hasprop(res, 'capabilities'):
1184 if not self._hasprop(res, "capabilities"):
1025 # Server doesn't support capabilities, so we need to
1185 # Server doesn't support capabilities, so we need to
1026 # synthesize the results based on the version
1186 # synthesize the results based on the version
1027 capabilities.synthesize(res, optional)
1187 capabilities.synthesize(res, optional)
1028 if 'error' in res:
1188 if "error" in res:
1029 raise CommandError(res['error'])
1189 raise CommandError(res["error"])
1030
1190
1031 return res
1191 return res
1032
1192
@@ -128,38 +128,27 b' static PyObject* bserobj_getattrro(PyObj'
128 Py_ssize_t i, n;
128 Py_ssize_t i, n;
129 PyObject* name_bytes = NULL;
129 PyObject* name_bytes = NULL;
130 PyObject* ret = NULL;
130 PyObject* ret = NULL;
131 const char* namestr = NULL;
131 const char* namestr;
132
132
133 if (PyIndex_Check(name)) {
133 if (PyIndex_Check(name)) {
134 i = PyNumber_AsSsize_t(name, PyExc_IndexError);
134 i = PyNumber_AsSsize_t(name, PyExc_IndexError);
135 if (i == -1 && PyErr_Occurred()) {
135 if (i == -1 && PyErr_Occurred()) {
136 goto bail;
136 goto bail;
137 }
137 }
138 ret = PySequence_GetItem(obj->values, i);
139 goto bail;
140 }
138
141
139 if (i == 8 && PySequence_Size(obj->values) < 9) {
142 // We can be passed in Unicode objects here -- we don't support anything other
140 // Hack alert: Python 3 removed support for os.stat().st_mtime
143 // than UTF-8 for keys.
141 // being an integer.Instead, if you need an integer, you have to
144 if (PyUnicode_Check(name)) {
142 // use os.stat()[stat.ST_MTIME] instead. stat.ST_MTIME is 8, and
145 name_bytes = PyUnicode_AsUTF8String(name);
143 // our stat tuples are shorter than that, so we can detect
146 if (name_bytes == NULL) {
144 // requests for index 8 on tuples shorter than that and return
145 // st_mtime instead.
146 namestr = "st_mtime";
147 } else {
148 ret = PySequence_GetItem(obj->values, i);
149 goto bail;
147 goto bail;
150 }
148 }
149 namestr = PyBytes_AsString(name_bytes);
151 } else {
150 } else {
152 // We can be passed in Unicode objects here -- we don't support anything other
151 namestr = PyBytes_AsString(name);
153 // than UTF-8 for keys.
154 if (PyUnicode_Check(name)) {
155 name_bytes = PyUnicode_AsUTF8String(name);
156 if (name_bytes == NULL) {
157 goto bail;
158 }
159 namestr = PyBytes_AsString(name_bytes);
160 } else {
161 namestr = PyBytes_AsString(name);
162 }
163 }
152 }
164
153
165 if (namestr == NULL) {
154 if (namestr == NULL) {
@@ -1147,11 +1136,15 b' static PyObject* bser_loads(PyObject* se'
1147 }
1136 }
1148
1137
1149 static PyObject* bser_load(PyObject* self, PyObject* args, PyObject* kw) {
1138 static PyObject* bser_load(PyObject* self, PyObject* args, PyObject* kw) {
1150 PyObject *load, *string;
1139 PyObject* load;
1140 PyObject* load_method;
1141 PyObject* string;
1142 PyObject* load_method_args;
1143 PyObject* load_method_kwargs;
1151 PyObject* fp = NULL;
1144 PyObject* fp = NULL;
1152 PyObject* mutable_obj = NULL;
1145 PyObject* mutable_obj = NULL;
1153 const char* value_encoding = NULL;
1146 PyObject* value_encoding = NULL;
1154 const char* value_errors = NULL;
1147 PyObject* value_errors = NULL;
1155
1148
1156 static char* kw_list[] = {
1149 static char* kw_list[] = {
1157 "fp", "mutable", "value_encoding", "value_errors", NULL};
1150 "fp", "mutable", "value_encoding", "value_errors", NULL};
@@ -1159,7 +1152,7 b' static PyObject* bser_load(PyObject* sel'
1159 if (!PyArg_ParseTupleAndKeywords(
1152 if (!PyArg_ParseTupleAndKeywords(
1160 args,
1153 args,
1161 kw,
1154 kw,
1162 "OOzz:load",
1155 "O|OOO:load",
1163 kw_list,
1156 kw_list,
1164 &fp,
1157 &fp,
1165 &mutable_obj,
1158 &mutable_obj,
@@ -1172,8 +1165,33 b' static PyObject* bser_load(PyObject* sel'
1172 if (load == NULL) {
1165 if (load == NULL) {
1173 return NULL;
1166 return NULL;
1174 }
1167 }
1175 string = PyObject_CallMethod(
1168 load_method = PyObject_GetAttrString(load, "load");
1176 load, "load", "OOzz", fp, mutable_obj, value_encoding, value_errors);
1169 if (load_method == NULL) {
1170 return NULL;
1171 }
1172 // Mandatory method arguments
1173 load_method_args = Py_BuildValue("(O)", fp);
1174 if (load_method_args == NULL) {
1175 return NULL;
1176 }
1177 // Optional method arguments
1178 load_method_kwargs = PyDict_New();
1179 if (load_method_kwargs == NULL) {
1180 return NULL;
1181 }
1182 if (mutable_obj) {
1183 PyDict_SetItemString(load_method_kwargs, "mutable", mutable_obj);
1184 }
1185 if (value_encoding) {
1186 PyDict_SetItemString(load_method_kwargs, "value_encoding", value_encoding);
1187 }
1188 if (value_errors) {
1189 PyDict_SetItemString(load_method_kwargs, "value_errors", value_errors);
1190 }
1191 string = PyObject_Call(load_method, load_method_args, load_method_kwargs);
1192 Py_DECREF(load_method_kwargs);
1193 Py_DECREF(load_method_args);
1194 Py_DECREF(load_method);
1177 Py_DECREF(load);
1195 Py_DECREF(load);
1178 return string;
1196 return string;
1179 }
1197 }
@@ -26,20 +26,20 b''
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
29 # no unicode literals
30 from __future__ import absolute_import, division, print_function
33
31
34 import re
32 import re
35
33
34
36 def parse_version(vstr):
35 def parse_version(vstr):
37 res = 0
36 res = 0
38 for n in vstr.split('.'):
37 for n in vstr.split("."):
39 res = res * 1000
38 res = res * 1000
40 res = res + int(n)
39 res = res + int(n)
41 return res
40 return res
42
41
42
43 cap_versions = {
43 cap_versions = {
44 "cmd-watch-del-all": "3.1.1",
44 "cmd-watch-del-all": "3.1.1",
45 "cmd-watch-project": "3.1",
45 "cmd-watch-project": "3.1",
@@ -49,23 +49,29 b' cap_versions = {'
49 "wildmatch": "3.7",
49 "wildmatch": "3.7",
50 }
50 }
51
51
52
52 def check(version, name):
53 def check(version, name):
53 if name in cap_versions:
54 if name in cap_versions:
54 return version >= parse_version(cap_versions[name])
55 return version >= parse_version(cap_versions[name])
55 return False
56 return False
56
57
58
57 def synthesize(vers, opts):
59 def synthesize(vers, opts):
58 """ Synthesize a capability enabled version response
60 """ Synthesize a capability enabled version response
59 This is a very limited emulation for relatively recent feature sets
61 This is a very limited emulation for relatively recent feature sets
60 """
62 """
61 parsed_version = parse_version(vers['version'])
63 parsed_version = parse_version(vers["version"])
62 vers['capabilities'] = {}
64 vers["capabilities"] = {}
63 for name in opts['optional']:
65 for name in opts["optional"]:
64 vers['capabilities'][name] = check(parsed_version, name)
66 vers["capabilities"][name] = check(parsed_version, name)
65 for name in opts['required']:
67 failed = False # noqa: F841 T25377293 Grandfathered in
68 for name in opts["required"]:
66 have = check(parsed_version, name)
69 have = check(parsed_version, name)
67 vers['capabilities'][name] = have
70 vers["capabilities"][name] = have
68 if not have:
71 if not have:
69 vers['error'] = 'client required capability `' + name + \
72 vers["error"] = (
70 '` is not supported by this server'
73 "client required capability `"
74 + name
75 + "` is not supported by this server"
76 )
71 return vers
77 return vers
@@ -26,20 +26,22 b''
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
29 # no unicode literals
33
30 from __future__ import absolute_import, division, print_function
34 '''Compatibility module across Python 2 and 3.'''
35
31
36 import sys
32 import sys
37
33
34
35 """Compatibility module across Python 2 and 3."""
36
37
38 PYTHON2 = sys.version_info < (3, 0)
38 PYTHON3 = sys.version_info >= (3, 0)
39 PYTHON3 = sys.version_info >= (3, 0)
39
40
40 # This is adapted from https://bitbucket.org/gutworth/six, and used under the
41 # This is adapted from https://bitbucket.org/gutworth/six, and used under the
41 # MIT license. See LICENSE for a full copyright notice.
42 # MIT license. See LICENSE for a full copyright notice.
42 if PYTHON3:
43 if PYTHON3:
44
43 def reraise(tp, value, tb=None):
45 def reraise(tp, value, tb=None):
44 try:
46 try:
45 if value is None:
47 if value is None:
@@ -50,16 +52,20 b' if PYTHON3:'
50 finally:
52 finally:
51 value = None
53 value = None
52 tb = None
54 tb = None
55
56
53 else:
57 else:
54 exec('''
58 exec(
59 """
55 def reraise(tp, value, tb=None):
60 def reraise(tp, value, tb=None):
56 try:
61 try:
57 raise tp, value, tb
62 raise tp, value, tb
58 finally:
63 finally:
59 tb = None
64 tb = None
60 '''.strip())
65 """.strip()
66 )
61
67
62 if PYTHON3:
68 if PYTHON3:
63 UNICODE = str
69 UNICODE = str
64 else:
70 else:
65 UNICODE = unicode
71 UNICODE = unicode # noqa: F821 We handled versioning above
@@ -26,48 +26,50 b''
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
29 # no unicode literals
33
30 from __future__ import absolute_import, division, print_function
34 '''Module to deal with filename encoding on the local system, as returned by
35 Watchman.'''
36
31
37 import sys
32 import sys
38
33
39 from . import (
34 from . import compat
40 compat,
35
41 )
36
37 """Module to deal with filename encoding on the local system, as returned by
38 Watchman."""
39
42
40
43 if compat.PYTHON3:
41 if compat.PYTHON3:
44 default_local_errors = 'surrogateescape'
42 default_local_errors = "surrogateescape"
45
43
46 def get_local_encoding():
44 def get_local_encoding():
47 if sys.platform == 'win32':
45 if sys.platform == "win32":
48 # Watchman always returns UTF-8 encoded strings on Windows.
46 # Watchman always returns UTF-8 encoded strings on Windows.
49 return 'utf-8'
47 return "utf-8"
50 # On the Python 3 versions we support, sys.getfilesystemencoding never
48 # On the Python 3 versions we support, sys.getfilesystemencoding never
51 # returns None.
49 # returns None.
52 return sys.getfilesystemencoding()
50 return sys.getfilesystemencoding()
51
52
53 else:
53 else:
54 # Python 2 doesn't support surrogateescape, so use 'strict' by
54 # Python 2 doesn't support surrogateescape, so use 'strict' by
55 # default. Users can register a custom surrogateescape error handler and use
55 # default. Users can register a custom surrogateescape error handler and use
56 # that if they so desire.
56 # that if they so desire.
57 default_local_errors = 'strict'
57 default_local_errors = "strict"
58
58
59 def get_local_encoding():
59 def get_local_encoding():
60 if sys.platform == 'win32':
60 if sys.platform == "win32":
61 # Watchman always returns UTF-8 encoded strings on Windows.
61 # Watchman always returns UTF-8 encoded strings on Windows.
62 return 'utf-8'
62 return "utf-8"
63 fsencoding = sys.getfilesystemencoding()
63 fsencoding = sys.getfilesystemencoding()
64 if fsencoding is None:
64 if fsencoding is None:
65 # This is very unlikely to happen, but if it does, just use UTF-8
65 # This is very unlikely to happen, but if it does, just use UTF-8
66 fsencoding = 'utf-8'
66 fsencoding = "utf-8"
67 return fsencoding
67 return fsencoding
68
68
69
69 def encode_local(s):
70 def encode_local(s):
70 return s.encode(get_local_encoding(), default_local_errors)
71 return s.encode(get_local_encoding(), default_local_errors)
71
72
73
72 def decode_local(bs):
74 def decode_local(bs):
73 return bs.decode(get_local_encoding(), default_local_errors)
75 return bs.decode(get_local_encoding(), default_local_errors)
@@ -26,17 +26,17 b''
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
29 # no unicode literals
30 from __future__ import absolute_import, division, print_function
31
32 import ctypes
33
33
34
34 try:
35 try:
35 from . import bser
36 from . import bser
36 except ImportError:
37 except ImportError:
37 from . import pybser as bser
38 from . import pybser as bser
38
39
39 import ctypes
40
40
41 EMPTY_HEADER = b"\x00\x01\x05\x00\x00\x00\x00"
41 EMPTY_HEADER = b"\x00\x01\x05\x00\x00\x00\x00"
42
42
@@ -95,13 +95,15 b' def load(fp, mutable=True, value_encodin'
95 ctypes.resize(buf, total_len)
95 ctypes.resize(buf, total_len)
96
96
97 body = (ctypes.c_char * (total_len - len(header))).from_buffer(
97 body = (ctypes.c_char * (total_len - len(header))).from_buffer(
98 buf, len(header))
98 buf, len(header)
99 )
99 read_len = _read_bytes(fp, body)
100 read_len = _read_bytes(fp, body)
100 if read_len < len(body):
101 if read_len < len(body):
101 raise RuntimeError('bser data ended early')
102 raise RuntimeError("bser data ended early")
102
103
103 return bser.loads(
104 return bser.loads(
104 (ctypes.c_char * total_len).from_buffer(buf, 0),
105 (ctypes.c_char * total_len).from_buffer(buf, 0),
105 mutable,
106 mutable,
106 value_encoding,
107 value_encoding,
107 value_errors)
108 value_errors,
109 )
@@ -26,10 +26,8 b''
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
29 # no unicode literals
30 from __future__ import absolute_import, division, print_function
33
31
34 import binascii
32 import binascii
35 import collections
33 import collections
@@ -37,30 +35,31 b' import ctypes'
37 import struct
35 import struct
38 import sys
36 import sys
39
37
40 from . import (
38 from . import compat
41 compat,
39
42 )
43
40
44 BSER_ARRAY = b'\x00'
41 BSER_ARRAY = b"\x00"
45 BSER_OBJECT = b'\x01'
42 BSER_OBJECT = b"\x01"
46 BSER_BYTESTRING = b'\x02'
43 BSER_BYTESTRING = b"\x02"
47 BSER_INT8 = b'\x03'
44 BSER_INT8 = b"\x03"
48 BSER_INT16 = b'\x04'
45 BSER_INT16 = b"\x04"
49 BSER_INT32 = b'\x05'
46 BSER_INT32 = b"\x05"
50 BSER_INT64 = b'\x06'
47 BSER_INT64 = b"\x06"
51 BSER_REAL = b'\x07'
48 BSER_REAL = b"\x07"
52 BSER_TRUE = b'\x08'
49 BSER_TRUE = b"\x08"
53 BSER_FALSE = b'\x09'
50 BSER_FALSE = b"\x09"
54 BSER_NULL = b'\x0a'
51 BSER_NULL = b"\x0a"
55 BSER_TEMPLATE = b'\x0b'
52 BSER_TEMPLATE = b"\x0b"
56 BSER_SKIP = b'\x0c'
53 BSER_SKIP = b"\x0c"
57 BSER_UTF8STRING = b'\x0d'
54 BSER_UTF8STRING = b"\x0d"
58
55
59 if compat.PYTHON3:
56 if compat.PYTHON3:
60 STRING_TYPES = (str, bytes)
57 STRING_TYPES = (str, bytes)
61 unicode = str
58 unicode = str
59
62 def tobytes(i):
60 def tobytes(i):
63 return str(i).encode('ascii')
61 return str(i).encode("ascii")
62
64 long = int
63 long = int
65 else:
64 else:
66 STRING_TYPES = (unicode, str)
65 STRING_TYPES = (unicode, str)
@@ -72,6 +71,7 b' else:'
72 EMPTY_HEADER = b"\x00\x01\x05\x00\x00\x00\x00"
71 EMPTY_HEADER = b"\x00\x01\x05\x00\x00\x00\x00"
73 EMPTY_HEADER_V2 = b"\x00\x02\x00\x00\x00\x00\x05\x00\x00\x00\x00"
72 EMPTY_HEADER_V2 = b"\x00\x02\x00\x00\x00\x00\x05\x00\x00\x00\x00"
74
73
74
75 def _int_size(x):
75 def _int_size(x):
76 """Return the smallest size int that can store the value"""
76 """Return the smallest size int that can store the value"""
77 if -0x80 <= x <= 0x7F:
77 if -0x80 <= x <= 0x7F:
@@ -83,29 +83,34 b' def _int_size(x):'
83 elif long(-0x8000000000000000) <= x <= long(0x7FFFFFFFFFFFFFFF):
83 elif long(-0x8000000000000000) <= x <= long(0x7FFFFFFFFFFFFFFF):
84 return 8
84 return 8
85 else:
85 else:
86 raise RuntimeError('Cannot represent value: ' + str(x))
86 raise RuntimeError("Cannot represent value: " + str(x))
87
87
88
88 def _buf_pos(buf, pos):
89 def _buf_pos(buf, pos):
89 ret = buf[pos]
90 ret = buf[pos]
90 # In Python 2, buf is a str array so buf[pos] is a string. In Python 3, buf
91 # Normalize the return type to bytes
91 # is a bytes array and buf[pos] is an integer.
92 if compat.PYTHON3 and not isinstance(ret, bytes):
92 if compat.PYTHON3:
93 ret = bytes((ret,))
93 ret = bytes((ret,))
94 return ret
94 return ret
95
95
96
96 class _bser_buffer(object):
97 class _bser_buffer(object):
97
98 def __init__(self, version):
98 def __init__(self, version):
99 self.bser_version = version
99 self.bser_version = version
100 self.buf = ctypes.create_string_buffer(8192)
100 self.buf = ctypes.create_string_buffer(8192)
101 if self.bser_version == 1:
101 if self.bser_version == 1:
102 struct.pack_into(tobytes(len(EMPTY_HEADER)) + b's', self.buf, 0,
102 struct.pack_into(
103 EMPTY_HEADER)
103 tobytes(len(EMPTY_HEADER)) + b"s", self.buf, 0, EMPTY_HEADER
104 )
104 self.wpos = len(EMPTY_HEADER)
105 self.wpos = len(EMPTY_HEADER)
105 else:
106 else:
106 assert self.bser_version == 2
107 assert self.bser_version == 2
107 struct.pack_into(tobytes(len(EMPTY_HEADER_V2)) + b's', self.buf, 0,
108 struct.pack_into(
108 EMPTY_HEADER_V2)
109 tobytes(len(EMPTY_HEADER_V2)) + b"s",
110 self.buf,
111 0,
112 EMPTY_HEADER_V2,
113 )
109 self.wpos = len(EMPTY_HEADER_V2)
114 self.wpos = len(EMPTY_HEADER_V2)
110
115
111 def ensure_size(self, size):
116 def ensure_size(self, size):
@@ -117,42 +122,68 b' class _bser_buffer(object):'
117 to_write = size + 1
122 to_write = size + 1
118 self.ensure_size(to_write)
123 self.ensure_size(to_write)
119 if size == 1:
124 if size == 1:
120 struct.pack_into(b'=cb', self.buf, self.wpos, BSER_INT8, val)
125 struct.pack_into(b"=cb", self.buf, self.wpos, BSER_INT8, val)
121 elif size == 2:
126 elif size == 2:
122 struct.pack_into(b'=ch', self.buf, self.wpos, BSER_INT16, val)
127 struct.pack_into(b"=ch", self.buf, self.wpos, BSER_INT16, val)
123 elif size == 4:
128 elif size == 4:
124 struct.pack_into(b'=ci', self.buf, self.wpos, BSER_INT32, val)
129 struct.pack_into(b"=ci", self.buf, self.wpos, BSER_INT32, val)
125 elif size == 8:
130 elif size == 8:
126 struct.pack_into(b'=cq', self.buf, self.wpos, BSER_INT64, val)
131 struct.pack_into(b"=cq", self.buf, self.wpos, BSER_INT64, val)
127 else:
132 else:
128 raise RuntimeError('Cannot represent this long value')
133 raise RuntimeError("Cannot represent this long value")
129 self.wpos += to_write
134 self.wpos += to_write
130
135
131
132 def append_string(self, s):
136 def append_string(self, s):
133 if isinstance(s, unicode):
137 if isinstance(s, unicode):
134 s = s.encode('utf-8')
138 s = s.encode("utf-8")
135 s_len = len(s)
139 s_len = len(s)
136 size = _int_size(s_len)
140 size = _int_size(s_len)
137 to_write = 2 + size + s_len
141 to_write = 2 + size + s_len
138 self.ensure_size(to_write)
142 self.ensure_size(to_write)
139 if size == 1:
143 if size == 1:
140 struct.pack_into(b'=ccb' + tobytes(s_len) + b's', self.buf,
144 struct.pack_into(
141 self.wpos, BSER_BYTESTRING, BSER_INT8, s_len, s)
145 b"=ccb" + tobytes(s_len) + b"s",
146 self.buf,
147 self.wpos,
148 BSER_BYTESTRING,
149 BSER_INT8,
150 s_len,
151 s,
152 )
142 elif size == 2:
153 elif size == 2:
143 struct.pack_into(b'=cch' + tobytes(s_len) + b's', self.buf,
154 struct.pack_into(
144 self.wpos, BSER_BYTESTRING, BSER_INT16, s_len, s)
155 b"=cch" + tobytes(s_len) + b"s",
156 self.buf,
157 self.wpos,
158 BSER_BYTESTRING,
159 BSER_INT16,
160 s_len,
161 s,
162 )
145 elif size == 4:
163 elif size == 4:
146 struct.pack_into(b'=cci' + tobytes(s_len) + b's', self.buf,
164 struct.pack_into(
147 self.wpos, BSER_BYTESTRING, BSER_INT32, s_len, s)
165 b"=cci" + tobytes(s_len) + b"s",
166 self.buf,
167 self.wpos,
168 BSER_BYTESTRING,
169 BSER_INT32,
170 s_len,
171 s,
172 )
148 elif size == 8:
173 elif size == 8:
149 struct.pack_into(b'=ccq' + tobytes(s_len) + b's', self.buf,
174 struct.pack_into(
150 self.wpos, BSER_BYTESTRING, BSER_INT64, s_len, s)
175 b"=ccq" + tobytes(s_len) + b"s",
176 self.buf,
177 self.wpos,
178 BSER_BYTESTRING,
179 BSER_INT64,
180 s_len,
181 s,
182 )
151 else:
183 else:
152 raise RuntimeError('Cannot represent this string value')
184 raise RuntimeError("Cannot represent this string value")
153 self.wpos += to_write
185 self.wpos += to_write
154
186
155
156 def append_recursive(self, val):
187 def append_recursive(self, val):
157 if isinstance(val, bool):
188 if isinstance(val, bool):
158 needed = 1
189 needed = 1
@@ -161,12 +192,12 b' class _bser_buffer(object):'
161 to_encode = BSER_TRUE
192 to_encode = BSER_TRUE
162 else:
193 else:
163 to_encode = BSER_FALSE
194 to_encode = BSER_FALSE
164 struct.pack_into(b'=c', self.buf, self.wpos, to_encode)
195 struct.pack_into(b"=c", self.buf, self.wpos, to_encode)
165 self.wpos += needed
196 self.wpos += needed
166 elif val is None:
197 elif val is None:
167 needed = 1
198 needed = 1
168 self.ensure_size(needed)
199 self.ensure_size(needed)
169 struct.pack_into(b'=c', self.buf, self.wpos, BSER_NULL)
200 struct.pack_into(b"=c", self.buf, self.wpos, BSER_NULL)
170 self.wpos += needed
201 self.wpos += needed
171 elif isinstance(val, (int, long)):
202 elif isinstance(val, (int, long)):
172 self.append_long(val)
203 self.append_long(val)
@@ -175,61 +206,106 b' class _bser_buffer(object):'
175 elif isinstance(val, float):
206 elif isinstance(val, float):
176 needed = 9
207 needed = 9
177 self.ensure_size(needed)
208 self.ensure_size(needed)
178 struct.pack_into(b'=cd', self.buf, self.wpos, BSER_REAL, val)
209 struct.pack_into(b"=cd", self.buf, self.wpos, BSER_REAL, val)
179 self.wpos += needed
210 self.wpos += needed
180 elif isinstance(val, collections.Mapping) and \
211 elif isinstance(val, collections.Mapping) and isinstance(
181 isinstance(val, collections.Sized):
212 val, collections.Sized
213 ):
182 val_len = len(val)
214 val_len = len(val)
183 size = _int_size(val_len)
215 size = _int_size(val_len)
184 needed = 2 + size
216 needed = 2 + size
185 self.ensure_size(needed)
217 self.ensure_size(needed)
186 if size == 1:
218 if size == 1:
187 struct.pack_into(b'=ccb', self.buf, self.wpos, BSER_OBJECT,
219 struct.pack_into(
188 BSER_INT8, val_len)
220 b"=ccb",
221 self.buf,
222 self.wpos,
223 BSER_OBJECT,
224 BSER_INT8,
225 val_len,
226 )
189 elif size == 2:
227 elif size == 2:
190 struct.pack_into(b'=cch', self.buf, self.wpos, BSER_OBJECT,
228 struct.pack_into(
191 BSER_INT16, val_len)
229 b"=cch",
230 self.buf,
231 self.wpos,
232 BSER_OBJECT,
233 BSER_INT16,
234 val_len,
235 )
192 elif size == 4:
236 elif size == 4:
193 struct.pack_into(b'=cci', self.buf, self.wpos, BSER_OBJECT,
237 struct.pack_into(
194 BSER_INT32, val_len)
238 b"=cci",
239 self.buf,
240 self.wpos,
241 BSER_OBJECT,
242 BSER_INT32,
243 val_len,
244 )
195 elif size == 8:
245 elif size == 8:
196 struct.pack_into(b'=ccq', self.buf, self.wpos, BSER_OBJECT,
246 struct.pack_into(
197 BSER_INT64, val_len)
247 b"=ccq",
248 self.buf,
249 self.wpos,
250 BSER_OBJECT,
251 BSER_INT64,
252 val_len,
253 )
198 else:
254 else:
199 raise RuntimeError('Cannot represent this mapping value')
255 raise RuntimeError("Cannot represent this mapping value")
200 self.wpos += needed
256 self.wpos += needed
201 if compat.PYTHON3:
257 if compat.PYTHON3:
202 iteritems = val.items()
258 iteritems = val.items()
203 else:
259 else:
204 iteritems = val.iteritems()
260 iteritems = val.iteritems() # noqa: B301 Checked version above
205 for k, v in iteritems:
261 for k, v in iteritems:
206 self.append_string(k)
262 self.append_string(k)
207 self.append_recursive(v)
263 self.append_recursive(v)
208 elif isinstance(val, collections.Iterable) and \
264 elif isinstance(val, collections.Iterable) and isinstance(
209 isinstance(val, collections.Sized):
265 val, collections.Sized
266 ):
210 val_len = len(val)
267 val_len = len(val)
211 size = _int_size(val_len)
268 size = _int_size(val_len)
212 needed = 2 + size
269 needed = 2 + size
213 self.ensure_size(needed)
270 self.ensure_size(needed)
214 if size == 1:
271 if size == 1:
215 struct.pack_into(b'=ccb', self.buf, self.wpos, BSER_ARRAY,
272 struct.pack_into(
216 BSER_INT8, val_len)
273 b"=ccb", self.buf, self.wpos, BSER_ARRAY, BSER_INT8, val_len
274 )
217 elif size == 2:
275 elif size == 2:
218 struct.pack_into(b'=cch', self.buf, self.wpos, BSER_ARRAY,
276 struct.pack_into(
219 BSER_INT16, val_len)
277 b"=cch",
278 self.buf,
279 self.wpos,
280 BSER_ARRAY,
281 BSER_INT16,
282 val_len,
283 )
220 elif size == 4:
284 elif size == 4:
221 struct.pack_into(b'=cci', self.buf, self.wpos, BSER_ARRAY,
285 struct.pack_into(
222 BSER_INT32, val_len)
286 b"=cci",
287 self.buf,
288 self.wpos,
289 BSER_ARRAY,
290 BSER_INT32,
291 val_len,
292 )
223 elif size == 8:
293 elif size == 8:
224 struct.pack_into(b'=ccq', self.buf, self.wpos, BSER_ARRAY,
294 struct.pack_into(
225 BSER_INT64, val_len)
295 b"=ccq",
296 self.buf,
297 self.wpos,
298 BSER_ARRAY,
299 BSER_INT64,
300 val_len,
301 )
226 else:
302 else:
227 raise RuntimeError('Cannot represent this sequence value')
303 raise RuntimeError("Cannot represent this sequence value")
228 self.wpos += needed
304 self.wpos += needed
229 for v in val:
305 for v in val:
230 self.append_recursive(v)
306 self.append_recursive(v)
231 else:
307 else:
232 raise RuntimeError('Cannot represent unknown value type')
308 raise RuntimeError("Cannot represent unknown value type")
233
309
234
310
235 def dumps(obj, version=1, capabilities=0):
311 def dumps(obj, version=1, capabilities=0):
@@ -238,18 +314,19 b' def dumps(obj, version=1, capabilities=0'
238 # Now fill in the overall length
314 # Now fill in the overall length
239 if version == 1:
315 if version == 1:
240 obj_len = bser_buf.wpos - len(EMPTY_HEADER)
316 obj_len = bser_buf.wpos - len(EMPTY_HEADER)
241 struct.pack_into(b'=i', bser_buf.buf, 3, obj_len)
317 struct.pack_into(b"=i", bser_buf.buf, 3, obj_len)
242 else:
318 else:
243 obj_len = bser_buf.wpos - len(EMPTY_HEADER_V2)
319 obj_len = bser_buf.wpos - len(EMPTY_HEADER_V2)
244 struct.pack_into(b'=i', bser_buf.buf, 2, capabilities)
320 struct.pack_into(b"=i", bser_buf.buf, 2, capabilities)
245 struct.pack_into(b'=i', bser_buf.buf, 7, obj_len)
321 struct.pack_into(b"=i", bser_buf.buf, 7, obj_len)
246 return bser_buf.buf.raw[:bser_buf.wpos]
322 return bser_buf.buf.raw[: bser_buf.wpos]
323
247
324
248 # This is a quack-alike with the bserObjectType in bser.c
325 # This is a quack-alike with the bserObjectType in bser.c
249 # It provides by getattr accessors and getitem for both index
326 # It provides by getattr accessors and getitem for both index
250 # and name.
327 # and name.
251 class _BunserDict(object):
328 class _BunserDict(object):
252 __slots__ = ('_keys', '_values')
329 __slots__ = ("_keys", "_values")
253
330
254 def __init__(self, keys, values):
331 def __init__(self, keys, values):
255 self._keys = keys
332 self._keys = keys
@@ -261,18 +338,19 b' class _BunserDict(object):'
261 def __getitem__(self, key):
338 def __getitem__(self, key):
262 if isinstance(key, (int, long)):
339 if isinstance(key, (int, long)):
263 return self._values[key]
340 return self._values[key]
264 elif key.startswith('st_'):
341 elif key.startswith("st_"):
265 # hack^Wfeature to allow mercurial to use "st_size" to
342 # hack^Wfeature to allow mercurial to use "st_size" to
266 # reference "size"
343 # reference "size"
267 key = key[3:]
344 key = key[3:]
268 try:
345 try:
269 return self._values[self._keys.index(key)]
346 return self._values[self._keys.index(key)]
270 except ValueError:
347 except ValueError:
271 raise KeyError('_BunserDict has no key %s' % key)
348 raise KeyError("_BunserDict has no key %s" % key)
272
349
273 def __len__(self):
350 def __len__(self):
274 return len(self._keys)
351 return len(self._keys)
275
352
353
276 class Bunser(object):
354 class Bunser(object):
277 def __init__(self, mutable=True, value_encoding=None, value_errors=None):
355 def __init__(self, mutable=True, value_encoding=None, value_errors=None):
278 self.mutable = mutable
356 self.mutable = mutable
@@ -281,7 +359,7 b' class Bunser(object):'
281 if value_encoding is None:
359 if value_encoding is None:
282 self.value_errors = None
360 self.value_errors = None
283 elif value_errors is None:
361 elif value_errors is None:
284 self.value_errors = 'strict'
362 self.value_errors = "strict"
285 else:
363 else:
286 self.value_errors = value_errors
364 self.value_errors = value_errors
287
365
@@ -290,33 +368,35 b' class Bunser(object):'
290 try:
368 try:
291 int_type = _buf_pos(buf, pos)
369 int_type = _buf_pos(buf, pos)
292 except IndexError:
370 except IndexError:
293 raise ValueError('Invalid bser int encoding, pos out of range')
371 raise ValueError("Invalid bser int encoding, pos out of range")
294 if int_type == BSER_INT8:
372 if int_type == BSER_INT8:
295 needed = 2
373 needed = 2
296 fmt = b'=b'
374 fmt = b"=b"
297 elif int_type == BSER_INT16:
375 elif int_type == BSER_INT16:
298 needed = 3
376 needed = 3
299 fmt = b'=h'
377 fmt = b"=h"
300 elif int_type == BSER_INT32:
378 elif int_type == BSER_INT32:
301 needed = 5
379 needed = 5
302 fmt = b'=i'
380 fmt = b"=i"
303 elif int_type == BSER_INT64:
381 elif int_type == BSER_INT64:
304 needed = 9
382 needed = 9
305 fmt = b'=q'
383 fmt = b"=q"
306 else:
384 else:
307 raise ValueError('Invalid bser int encoding 0x%s' %
385 raise ValueError(
308 binascii.hexlify(int_type).decode('ascii'))
386 "Invalid bser int encoding 0x%s at position %s"
387 % (binascii.hexlify(int_type).decode("ascii"), pos)
388 )
309 int_val = struct.unpack_from(fmt, buf, pos + 1)[0]
389 int_val = struct.unpack_from(fmt, buf, pos + 1)[0]
310 return (int_val, pos + needed)
390 return (int_val, pos + needed)
311
391
312 def unser_utf8_string(self, buf, pos):
392 def unser_utf8_string(self, buf, pos):
313 str_len, pos = self.unser_int(buf, pos + 1)
393 str_len, pos = self.unser_int(buf, pos + 1)
314 str_val = struct.unpack_from(tobytes(str_len) + b's', buf, pos)[0]
394 str_val = struct.unpack_from(tobytes(str_len) + b"s", buf, pos)[0]
315 return (str_val.decode('utf-8'), pos + str_len)
395 return (str_val.decode("utf-8"), pos + str_len)
316
396
317 def unser_bytestring(self, buf, pos):
397 def unser_bytestring(self, buf, pos):
318 str_len, pos = self.unser_int(buf, pos + 1)
398 str_len, pos = self.unser_int(buf, pos + 1)
319 str_val = struct.unpack_from(tobytes(str_len) + b's', buf, pos)[0]
399 str_val = struct.unpack_from(tobytes(str_len) + b"s", buf, pos)[0]
320 if self.value_encoding is not None:
400 if self.value_encoding is not None:
321 str_val = str_val.decode(self.value_encoding, self.value_errors)
401 str_val = str_val.decode(self.value_encoding, self.value_errors)
322 # str_len stays the same because that's the length in bytes
402 # str_len stays the same because that's the length in bytes
@@ -325,12 +405,12 b' class Bunser(object):'
325 def unser_array(self, buf, pos):
405 def unser_array(self, buf, pos):
326 arr_len, pos = self.unser_int(buf, pos + 1)
406 arr_len, pos = self.unser_int(buf, pos + 1)
327 arr = []
407 arr = []
328 for i in range(arr_len):
408 for _ in range(arr_len):
329 arr_item, pos = self.loads_recursive(buf, pos)
409 arr_item, pos = self.loads_recursive(buf, pos)
330 arr.append(arr_item)
410 arr.append(arr_item)
331
411
332 if not self.mutable:
412 if not self.mutable:
333 arr = tuple(arr)
413 arr = tuple(arr)
334
414
335 return arr, pos
415 return arr, pos
336
416
@@ -342,7 +422,7 b' class Bunser(object):'
342 keys = []
422 keys = []
343 vals = []
423 vals = []
344
424
345 for i in range(obj_len):
425 for _ in range(obj_len):
346 key, pos = self.unser_utf8_string(buf, pos)
426 key, pos = self.unser_utf8_string(buf, pos)
347 val, pos = self.loads_recursive(buf, pos)
427 val, pos = self.loads_recursive(buf, pos)
348 if self.mutable:
428 if self.mutable:
@@ -359,13 +439,13 b' class Bunser(object):'
359 def unser_template(self, buf, pos):
439 def unser_template(self, buf, pos):
360 val_type = _buf_pos(buf, pos + 1)
440 val_type = _buf_pos(buf, pos + 1)
361 if val_type != BSER_ARRAY:
441 if val_type != BSER_ARRAY:
362 raise RuntimeError('Expect ARRAY to follow TEMPLATE')
442 raise RuntimeError("Expect ARRAY to follow TEMPLATE")
363 # force UTF-8 on keys
443 # force UTF-8 on keys
364 keys_bunser = Bunser(mutable=self.mutable, value_encoding='utf-8')
444 keys_bunser = Bunser(mutable=self.mutable, value_encoding="utf-8")
365 keys, pos = keys_bunser.unser_array(buf, pos + 1)
445 keys, pos = keys_bunser.unser_array(buf, pos + 1)
366 nitems, pos = self.unser_int(buf, pos)
446 nitems, pos = self.unser_int(buf, pos)
367 arr = []
447 arr = []
368 for i in range(nitems):
448 for _ in range(nitems):
369 if self.mutable:
449 if self.mutable:
370 obj = {}
450 obj = {}
371 else:
451 else:
@@ -392,11 +472,15 b' class Bunser(object):'
392
472
393 def loads_recursive(self, buf, pos):
473 def loads_recursive(self, buf, pos):
394 val_type = _buf_pos(buf, pos)
474 val_type = _buf_pos(buf, pos)
395 if (val_type == BSER_INT8 or val_type == BSER_INT16 or
475 if (
396 val_type == BSER_INT32 or val_type == BSER_INT64):
476 val_type == BSER_INT8
477 or val_type == BSER_INT16
478 or val_type == BSER_INT32
479 or val_type == BSER_INT64
480 ):
397 return self.unser_int(buf, pos)
481 return self.unser_int(buf, pos)
398 elif val_type == BSER_REAL:
482 elif val_type == BSER_REAL:
399 val = struct.unpack_from(b'=d', buf, pos + 1)[0]
483 val = struct.unpack_from(b"=d", buf, pos + 1)[0]
400 return (val, pos + 9)
484 return (val, pos + 9)
401 elif val_type == BSER_TRUE:
485 elif val_type == BSER_TRUE:
402 return (True, pos + 1)
486 return (True, pos + 1)
@@ -415,23 +499,26 b' class Bunser(object):'
415 elif val_type == BSER_TEMPLATE:
499 elif val_type == BSER_TEMPLATE:
416 return self.unser_template(buf, pos)
500 return self.unser_template(buf, pos)
417 else:
501 else:
418 raise ValueError('unhandled bser opcode 0x%s' %
502 raise ValueError(
419 binascii.hexlify(val_type).decode('ascii'))
503 "unhandled bser opcode 0x%s"
504 % binascii.hexlify(val_type).decode("ascii")
505 )
420
506
421
507
422 def _pdu_info_helper(buf):
508 def _pdu_info_helper(buf):
509 bser_version = -1
423 if buf[0:2] == EMPTY_HEADER[0:2]:
510 if buf[0:2] == EMPTY_HEADER[0:2]:
424 bser_version = 1
511 bser_version = 1
425 bser_capabilities = 0
512 bser_capabilities = 0
426 expected_len, pos2 = Bunser.unser_int(buf, 2)
513 expected_len, pos2 = Bunser.unser_int(buf, 2)
427 elif buf[0:2] == EMPTY_HEADER_V2[0:2]:
514 elif buf[0:2] == EMPTY_HEADER_V2[0:2]:
428 if len(buf) < 8:
515 if len(buf) < 8:
429 raise ValueError('Invalid BSER header')
516 raise ValueError("Invalid BSER header")
430 bser_version = 2
517 bser_version = 2
431 bser_capabilities = struct.unpack_from("I", buf, 2)[0]
518 bser_capabilities = struct.unpack_from("I", buf, 2)[0]
432 expected_len, pos2 = Bunser.unser_int(buf, 6)
519 expected_len, pos2 = Bunser.unser_int(buf, 6)
433 else:
520 else:
434 raise ValueError('Invalid BSER header')
521 raise ValueError("Invalid BSER header")
435
522
436 return bser_version, bser_capabilities, expected_len, pos2
523 return bser_version, bser_capabilities, expected_len, pos2
437
524
@@ -470,14 +557,20 b' def loads(buf, mutable=True, value_encod'
470 pos = info[3]
557 pos = info[3]
471
558
472 if len(buf) != expected_len + pos:
559 if len(buf) != expected_len + pos:
473 raise ValueError('bser data len != header len')
560 raise ValueError(
561 "bser data len %d != header len %d" % (expected_len + pos, len(buf))
562 )
474
563
475 bunser = Bunser(mutable=mutable, value_encoding=value_encoding,
564 bunser = Bunser(
476 value_errors=value_errors)
565 mutable=mutable,
566 value_encoding=value_encoding,
567 value_errors=value_errors,
568 )
477
569
478 return bunser.loads_recursive(buf, pos)[0]
570 return bunser.loads_recursive(buf, pos)[0]
479
571
480
572
481 def load(fp, mutable=True, value_encoding=None, value_errors=None):
573 def load(fp, mutable=True, value_encoding=None, value_errors=None):
482 from . import load
574 from . import load
575
483 return load.load(fp, mutable, value_encoding, value_errors)
576 return load.load(fp, mutable, value_encoding, value_errors)
@@ -10,6 +10,7 b' from __future__ import absolute_import'
10 import getpass
10 import getpass
11
11
12 from mercurial import util
12 from mercurial import util
13 from mercurial.utils import procutil
13
14
14 from . import pywatchman
15 from . import pywatchman
15
16
@@ -92,7 +93,7 b' class client(object):'
92 self._watchmanclient = pywatchman.client(
93 self._watchmanclient = pywatchman.client(
93 timeout=self._timeout,
94 timeout=self._timeout,
94 useImmutableBser=True,
95 useImmutableBser=True,
95 watchman_exe=watchman_exe,
96 binpath=procutil.tonativestr(watchman_exe),
96 )
97 )
97 return self._watchmanclient.query(*watchmanargs)
98 return self._watchmanclient.query(*watchmanargs)
98 except pywatchman.CommandError as ex:
99 except pywatchman.CommandError as ex:
General Comments 0
You need to be logged in to leave comments. Login now