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