##// 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
@@ -1,1035 +1,1195 b''
1 1 # Copyright 2014-present Facebook, Inc.
2 2 # All rights reserved.
3 3 #
4 4 # Redistribution and use in source and binary forms, with or without
5 5 # modification, are permitted provided that the following conditions are met:
6 6 #
7 7 # * Redistributions of source code must retain the above copyright notice,
8 8 # this list of conditions and the following disclaimer.
9 9 #
10 10 # * Redistributions in binary form must reproduce the above copyright notice,
11 11 # this list of conditions and the following disclaimer in the documentation
12 12 # and/or other materials provided with the distribution.
13 13 #
14 14 # * Neither the name Facebook nor the names of its contributors may be used to
15 15 # endorse or promote products derived from this software without specific
16 16 # prior written permission.
17 17 #
18 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 19 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
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
36 34 import os
37 35 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
71 58 wintypes = ctypes.wintypes
72 59 GENERIC_READ = 0x80000000
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
80 67 WAIT_FAILED = 0xFFFFFFFF
81 68 WAIT_TIMEOUT = 0x00000102
82 69 WAIT_OBJECT_0 = 0x00000000
83 70 WAIT_IO_COMPLETION = 0x000000C0
84 71 INFINITE = 0xFFFFFFFF
85 72
86 73 # Overlapped I/O operation is in progress. (997)
87 74 ERROR_IO_PENDING = 0x000003E5
88 75
89 76 # The pointer size follows the architecture
90 77 # We use WPARAM since this type is already conditionally defined
91 78 ULONG_PTR = ctypes.wintypes.WPARAM
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):
101 90 self.Internal = 0
102 91 self.InternalHigh = 0
103 92 self.Offset = 0
104 93 self.OffsetHigh = 0
105 94 self.hEvent = 0
106 95
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
116 111 CloseHandle.argtypes = [wintypes.HANDLE]
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
130 135 GetLastError.argtypes = []
131 136 GetLastError.restype = wintypes.DWORD
132 137
133 138 SetLastError = ctypes.windll.kernel32.SetLastError
134 139 SetLastError.argtypes = [wintypes.DWORD]
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.
169 196 CancelIoEx = ctypes.windll.kernel32.CancelIoEx
170 197 CancelIoEx.argtypes = [wintypes.HANDLE, ctypes.POINTER(OVERLAPPED)]
171 198 CancelIoEx.restype = wintypes.BOOL
172 199
173 200 # 2 bytes marker, 1 byte int size, 8 bytes int64 value
174 201 sniff_len = 13
175 202
176 203 # This is a helper for debugging the client.
177 204 _debugging = False
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):
187 220 pass
188 221
189 222
190 223 def _win32_strerror(err):
191 224 """ expand a win32 error code into a human readable message """
192 225
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:
201 242 LocalFree(buf)
202 243
203 244
204 245 class WatchmanError(Exception):
205 246 def __init__(self, msg=None, cmd=None):
206 247 self.msg = msg
207 248 self.cmd = cmd
208 249
209 250 def setCommand(self, cmd):
210 251 self.cmd = cmd
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
232 282
233 283 class SocketTimeout(WatchmanError):
234 284 """A specialized exception raised for socket timeouts during communication to/from watchman.
235 285 This makes it easier to implement non-blocking loops as callers can easily distinguish
236 286 between a routine timeout and an actual error condition.
237 287
238 288 Note that catching WatchmanError will also catch this as it is a super-class, so backwards
239 289 compatibility in exception handling is preserved.
240 290 """
241 291
242 292
243 293 class CommandError(WatchmanError):
244 294 """error returned by watchman
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):
260 311 """ tear it down """
261 312 raise NotImplementedError()
262 313
263 314 def readBytes(self, size):
264 315 """ read size bytes """
265 316 raise NotImplementedError()
266 317
267 318 def write(self, buf):
268 319 """ write some data """
269 320 raise NotImplementedError()
270 321
271 322 def setTimeout(self, value):
272 323 pass
273 324
274 325 def readLine(self):
275 326 """ read a line
276 327 Maintains its own buffer, callers of the transport should not mix
277 328 calls to readBytes and readLine.
278 329 """
279 330 if self.buf is None:
280 331 self.buf = []
281 332
282 333 # Buffer may already have a line if we've received unilateral
283 334 # response(s) from the server
284 335 if len(self.buf) == 1 and b"\n" in self.buf[0]:
285 336 (line, b) = self.buf[0].split(b"\n", 1)
286 337 self.buf = [b]
287 338 return line
288 339
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
296 347 self.buf.append(b)
297 348
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):
304 356 self.transport = transport
305 357
306 358 def receive(self):
307 359 raise NotImplementedError()
308 360
309 361 def send(self, *args):
310 362 raise NotImplementedError()
311 363
312 364 def setTimeout(self, value):
313 365 self.transport.setTimeout(value)
314 366
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
324 377 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
325 378 try:
326 379 sock.settimeout(self.timeout)
327 380 sock.connect(self.sockpath)
328 381 self.sock = sock
329 382 except socket.error as e:
330 383 sock.close()
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
339 393 self.sock.settimeout(self.timeout)
340 394
341 395 def readBytes(self, size):
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):
358 412 """ Windows 7 and earlier does not support GetOverlappedResultEx. The
359 413 alternative is to use GetOverlappedResult and wait for read or write
360 414 operation to complete. This is done be using CreateEvent and
361 415 WaitForSingleObjectEx. CreateEvent, WaitForSingleObjectEx
362 416 and GetOverlappedResult are all part of Windows API since WindowsXP.
363 417 This is the exact same implementation that can be found in the watchman
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:
371 425 # Event is signaled, overlapped IO operation result should be available.
372 426 pass
373 427 elif waitReturnCode == WAIT_IO_COMPLETION:
374 428 # WaitForSingleObjectEx returnes because the system added an I/O completion
375 429 # routine or an asynchronous procedure call (APC) to the thread queue.
376 430 SetLastError(WAIT_IO_COMPLETION)
377 431 pass
378 432 elif waitReturnCode == WAIT_TIMEOUT:
379 433 # We reached the maximum allowed wait time, the IO operation failed
380 434 # to complete in timely fashion.
381 435 SetLastError(WAIT_TIMEOUT)
382 436 return False
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)
395 449
396 450
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
433 503 if self._waitable is not None:
434 504 # We release the handle for the event
435 505 CloseHandle(self._waitable)
436 506 self._waitable = None
437 507
438 508 def setTimeout(self, value):
439 509 # convert to milliseconds
440 510 self.timeout = int(value * 1000)
441 511
442 512 def readBytes(self, size):
443 513 """ A read can block for an unbounded amount of time, even if the
444 514 kernel reports that the pipe handle is signalled, so we need to
445 515 always perform our reads asynchronously
446 516 """
447 517
448 518 # try to satisfy the read from any buffered data
449 519 if self._iobuf:
450 520 if size >= len(self._iobuf):
451 521 res = self._iobuf
452 522 self.buf = None
453 523 return res
454 524 res = self._iobuf[:size]
455 525 self._iobuf = self._iobuf[size:]
456 526 return res
457 527
458 528 # We need to initiate a read
459 529 buf = ctypes.create_string_buffer(size)
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
467 537 immediate = ReadFile(self.pipe, buf, size, None, olap)
468 538
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:
492 562 # Docs say that named pipes return 0 byte when the other end did
493 563 # a zero byte write. Since we don't ever do that, the only
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]
501 571
502 572 returned_size = min(nread, size)
503 573 if returned_size == nread:
504 574 return buf
505 575
506 576 # keep any left-overs around for a later read to consume
507 577 self._iobuf = buf[returned_size:]
508 578 return buf[:returned_size]
509 579
510 580 def write(self, data):
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()
532 606
533 607 # It's potentially unsafe to allow the write to continue after
534 608 # we unwind, so let's make a best effort to avoid that happening
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):
545 630 """ open a pipe to the cli to talk to the service
546 631 This intended to be used only in the test harness!
547 632
548 633 The CLI is an oddball because we only support JSON input
549 634 and cannot send multiple commands through the same instance,
550 635 so we spawn a new process for each command.
551 636
552 637 We disable server spawning for this implementation, again, because
553 638 it is intended to be used only in our test harness. You really
554 639 should not need to use the CLI transport for anything real.
555 640
556 641 While the CLI can output in BSER, our Transport interface doesn't
557 642 support telling this instance that it should do so. That effectively
558 643 limits this implementation to JSON input and output only at this time.
559 644
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:
573 659 if self.proc.pid is not None:
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):
606 692 if self.closed:
607 693 self.close()
608 694 self.closed = False
609 695 self._connect()
610 696 res = self.proc.stdin.write(data)
611 697 self.proc.stdin.close()
612 698 self.closed = True
613 699 return res
614 700
615 701
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
629 724 rlen = len(buf[0])
630 725 while elen > rlen:
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
646 741 class ImmutableBserCodec(BserCodec):
647 742 """ use the BSER encoding, decoding values using the newer
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):
722 839 line = self.transport.readLine()
723 840 try:
724 841 # In Python 3, json.loads is a transformation from Unicode string to
725 842 # objects possibly containing Unicode strings. We typically expect
726 843 # the JSON blob to be ASCII-only with non-ASCII characters escaped,
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)
734 851 raise
735 852
736 853 def send(self, *args):
737 854 cmd = self.json.dumps(*args)
738 855 # In Python 3, json.dumps is a transformation from objects possibly
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
751 869 recvCodec = None
752 870 sendConn = None
753 871 recvConn = None
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:
814 972 return hasattr(result, name)
815 973 return name in result
816 974
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()
846 1003
847 1004 if exitcode:
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()
875 1048 self.tport = None
876 1049 self.recvConn = None
877 1050 self.sendConn = None
878 1051
879 1052 def receive(self):
880 1053 """ receive the next PDU from the watchman service
881 1054
882 1055 If the client has activated subscriptions or logs then
883 1056 this PDU may be a unilateral PDU sent by the service to
884 1057 inform the client of a log event or subscription change.
885 1058
886 1059 It may also simply be the response portion of a request
887 1060 initiated by query.
888 1061
889 1062 There are clients in production that subscribe and call
890 1063 this in a loop to retrieve all subscription responses,
891 1064 so care should be taken when making changes here.
892 1065 """
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]:
919 1086 self.sub_by_root[root][sub] = []
920 1087 self.sub_by_root[root][sub].append(result)
921 1088
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:
929 1096 if k in res:
930 1097 return True
931 1098 return False
932 1099
933 1100 def getLog(self, remove=True):
934 1101 """ Retrieve buffered log data
935 1102
936 1103 If remove is true the data will be removed from the buffer.
937 1104 Otherwise it will be left in the buffer
938 1105 """
939 1106 res = self.logs
940 1107 if remove:
941 1108 self.logs = []
942 1109 return res
943 1110
944 1111 def getSubscription(self, name, remove=True, root=None):
945 1112 """ Retrieve the data associated with a named subscription
946 1113
947 1114 If remove is True (the default), the subscription data is removed
948 1115 from the buffer. Otherwise the data is returned but left in
949 1116 the buffer.
950 1117
951 1118 Returns None if there is no data associated with `name`
952 1119
953 1120 If root is not None, then only return the subscription
954 1121 data that matches both root and name. When used in this way,
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:
973 1133 del self.sub_by_root[root][name]
974 1134 # don't let this grow unbounded
975 1135 if name in self.subs:
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:
983 1143 del self.subs[name]
984 1144 return sub
985 1145
986 1146 def query(self, *args):
987 1147 """ Send a query to the watchman service and return the response
988 1148
989 1149 This call will block until the response is returned.
990 1150 If any unilateral responses are sent by the service in between
991 1151 the request-response they will be buffered up in the 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)
999 1159
1000 1160 res = self.receive()
1001 1161 while self.isUnilateralResponse(res):
1002 1162 res = self.receive()
1003 1163
1004 1164 return res
1005 1165 except EnvironmentError as ee:
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
1033 1193 def setTimeout(self, value):
1034 1194 self.recvConn.setTimeout(value)
1035 1195 self.sendConn.setTimeout(value)
@@ -1,1223 +1,1241 b''
1 1 /*
2 2 Copyright (c) 2013-2015, Facebook, Inc.
3 3 All rights reserved.
4 4
5 5 Redistribution and use in source and binary forms, with or without
6 6 modification, are permitted provided that the following conditions are met:
7 7
8 8 * Redistributions of source code must retain the above copyright notice,
9 9 this list of conditions and the following disclaimer.
10 10
11 11 * Redistributions in binary form must reproduce the above copyright notice,
12 12 this list of conditions and the following disclaimer in the documentation
13 13 and/or other materials provided with the distribution.
14 14
15 15 * Neither the name Facebook nor the names of its contributors may be used to
16 16 endorse or promote products derived from this software without specific
17 17 prior written permission.
18 18
19 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 27 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 29 */
30 30
31 31 #include <Python.h>
32 32 #include <bytesobject.h>
33 33 #ifdef _MSC_VER
34 34 #define inline __inline
35 35 #if _MSC_VER >= 1800
36 36 #include <stdint.h>
37 37 #else
38 38 // The compiler associated with Python 2.7 on Windows doesn't ship
39 39 // with stdint.h, so define the small subset that we use here.
40 40 typedef __int8 int8_t;
41 41 typedef __int16 int16_t;
42 42 typedef __int32 int32_t;
43 43 typedef __int64 int64_t;
44 44 typedef unsigned __int8 uint8_t;
45 45 typedef unsigned __int16 uint16_t;
46 46 typedef unsigned __int32 uint32_t;
47 47 typedef unsigned __int64 uint64_t;
48 48 #define UINT32_MAX 4294967295U
49 49 #endif
50 50 #endif
51 51
52 52 // clang-format off
53 53 /* Return the smallest size int that can store the value */
54 54 #define INT_SIZE(x) (((x) == ((int8_t)x)) ? 1 : \
55 55 ((x) == ((int16_t)x)) ? 2 : \
56 56 ((x) == ((int32_t)x)) ? 4 : 8)
57 57
58 58 #define BSER_ARRAY 0x00
59 59 #define BSER_OBJECT 0x01
60 60 #define BSER_BYTESTRING 0x02
61 61 #define BSER_INT8 0x03
62 62 #define BSER_INT16 0x04
63 63 #define BSER_INT32 0x05
64 64 #define BSER_INT64 0x06
65 65 #define BSER_REAL 0x07
66 66 #define BSER_TRUE 0x08
67 67 #define BSER_FALSE 0x09
68 68 #define BSER_NULL 0x0a
69 69 #define BSER_TEMPLATE 0x0b
70 70 #define BSER_SKIP 0x0c
71 71 #define BSER_UTF8STRING 0x0d
72 72 // clang-format on
73 73
74 74 // An immutable object representation of BSER_OBJECT.
75 75 // Rather than build a hash table, key -> value are obtained
76 76 // by walking the list of keys to determine the offset into
77 77 // the values array. The assumption is that the number of
78 78 // array elements will be typically small (~6 for the top
79 79 // level query result and typically 3-5 for the file entries)
80 80 // so that the time overhead for this is small compared to
81 81 // using a proper hash table. Even with this simplistic
82 82 // approach, this is still faster for the mercurial use case
83 83 // as it helps to eliminate creating N other objects to
84 84 // represent the stat information in the hgwatchman extension
85 85 // clang-format off
86 86 typedef struct {
87 87 PyObject_HEAD
88 88 PyObject *keys; // tuple of field names
89 89 PyObject *values; // tuple of values
90 90 } bserObject;
91 91 // clang-format on
92 92
93 93 static Py_ssize_t bserobj_tuple_length(PyObject* o) {
94 94 bserObject* obj = (bserObject*)o;
95 95
96 96 return PySequence_Length(obj->keys);
97 97 }
98 98
99 99 static PyObject* bserobj_tuple_item(PyObject* o, Py_ssize_t i) {
100 100 bserObject* obj = (bserObject*)o;
101 101
102 102 return PySequence_GetItem(obj->values, i);
103 103 }
104 104
105 105 // clang-format off
106 106 static PySequenceMethods bserobj_sq = {
107 107 bserobj_tuple_length, /* sq_length */
108 108 0, /* sq_concat */
109 109 0, /* sq_repeat */
110 110 bserobj_tuple_item, /* sq_item */
111 111 0, /* sq_ass_item */
112 112 0, /* sq_contains */
113 113 0, /* sq_inplace_concat */
114 114 0 /* sq_inplace_repeat */
115 115 };
116 116 // clang-format on
117 117
118 118 static void bserobj_dealloc(PyObject* o) {
119 119 bserObject* obj = (bserObject*)o;
120 120
121 121 Py_CLEAR(obj->keys);
122 122 Py_CLEAR(obj->values);
123 123 PyObject_Del(o);
124 124 }
125 125
126 126 static PyObject* bserobj_getattrro(PyObject* o, PyObject* name) {
127 127 bserObject* obj = (bserObject*)o;
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) {
166 155 goto bail;
167 156 }
168 157 // hack^Wfeature to allow mercurial to use "st_size" to reference "size"
169 158 if (!strncmp(namestr, "st_", 3)) {
170 159 namestr += 3;
171 160 }
172 161
173 162 n = PyTuple_GET_SIZE(obj->keys);
174 163 for (i = 0; i < n; i++) {
175 164 const char* item_name = NULL;
176 165 PyObject* key = PyTuple_GET_ITEM(obj->keys, i);
177 166
178 167 item_name = PyBytes_AsString(key);
179 168 if (!strcmp(item_name, namestr)) {
180 169 ret = PySequence_GetItem(obj->values, i);
181 170 goto bail;
182 171 }
183 172 }
184 173
185 174 PyErr_Format(
186 175 PyExc_AttributeError, "bserobject has no attribute '%.400s'", namestr);
187 176 bail:
188 177 Py_XDECREF(name_bytes);
189 178 return ret;
190 179 }
191 180
192 181 // clang-format off
193 182 static PyMappingMethods bserobj_map = {
194 183 bserobj_tuple_length, /* mp_length */
195 184 bserobj_getattrro, /* mp_subscript */
196 185 0 /* mp_ass_subscript */
197 186 };
198 187
199 188 PyTypeObject bserObjectType = {
200 189 PyVarObject_HEAD_INIT(NULL, 0)
201 190 "bserobj_tuple", /* tp_name */
202 191 sizeof(bserObject), /* tp_basicsize */
203 192 0, /* tp_itemsize */
204 193 bserobj_dealloc, /* tp_dealloc */
205 194 0, /* tp_print */
206 195 0, /* tp_getattr */
207 196 0, /* tp_setattr */
208 197 0, /* tp_compare */
209 198 0, /* tp_repr */
210 199 0, /* tp_as_number */
211 200 &bserobj_sq, /* tp_as_sequence */
212 201 &bserobj_map, /* tp_as_mapping */
213 202 0, /* tp_hash */
214 203 0, /* tp_call */
215 204 0, /* tp_str */
216 205 bserobj_getattrro, /* tp_getattro */
217 206 0, /* tp_setattro */
218 207 0, /* tp_as_buffer */
219 208 Py_TPFLAGS_DEFAULT, /* tp_flags */
220 209 "bserobj tuple", /* tp_doc */
221 210 0, /* tp_traverse */
222 211 0, /* tp_clear */
223 212 0, /* tp_richcompare */
224 213 0, /* tp_weaklistoffset */
225 214 0, /* tp_iter */
226 215 0, /* tp_iternext */
227 216 0, /* tp_methods */
228 217 0, /* tp_members */
229 218 0, /* tp_getset */
230 219 0, /* tp_base */
231 220 0, /* tp_dict */
232 221 0, /* tp_descr_get */
233 222 0, /* tp_descr_set */
234 223 0, /* tp_dictoffset */
235 224 0, /* tp_init */
236 225 0, /* tp_alloc */
237 226 0, /* tp_new */
238 227 };
239 228 // clang-format on
240 229
241 230 typedef struct loads_ctx {
242 231 int mutable;
243 232 const char* value_encoding;
244 233 const char* value_errors;
245 234 uint32_t bser_version;
246 235 uint32_t bser_capabilities;
247 236 } unser_ctx_t;
248 237
249 238 static PyObject*
250 239 bser_loads_recursive(const char** ptr, const char* end, const unser_ctx_t* ctx);
251 240
252 241 static const char bser_true = BSER_TRUE;
253 242 static const char bser_false = BSER_FALSE;
254 243 static const char bser_null = BSER_NULL;
255 244 static const char bser_bytestring_hdr = BSER_BYTESTRING;
256 245 static const char bser_array_hdr = BSER_ARRAY;
257 246 static const char bser_object_hdr = BSER_OBJECT;
258 247
259 248 static inline uint32_t next_power_2(uint32_t n) {
260 249 n |= (n >> 16);
261 250 n |= (n >> 8);
262 251 n |= (n >> 4);
263 252 n |= (n >> 2);
264 253 n |= (n >> 1);
265 254 return n + 1;
266 255 }
267 256
268 257 // A buffer we use for building up the serialized result
269 258 struct bser_buffer {
270 259 char* buf;
271 260 int wpos, allocd;
272 261 uint32_t bser_version;
273 262 uint32_t capabilities;
274 263 };
275 264 typedef struct bser_buffer bser_t;
276 265
277 266 static int bser_append(bser_t* bser, const char* data, uint32_t len) {
278 267 int newlen = next_power_2(bser->wpos + len);
279 268 if (newlen > bser->allocd) {
280 269 char* nbuf = realloc(bser->buf, newlen);
281 270 if (!nbuf) {
282 271 return 0;
283 272 }
284 273
285 274 bser->buf = nbuf;
286 275 bser->allocd = newlen;
287 276 }
288 277
289 278 memcpy(bser->buf + bser->wpos, data, len);
290 279 bser->wpos += len;
291 280 return 1;
292 281 }
293 282
294 283 static int bser_init(bser_t* bser, uint32_t version, uint32_t capabilities) {
295 284 bser->allocd = 8192;
296 285 bser->wpos = 0;
297 286 bser->buf = malloc(bser->allocd);
298 287 bser->bser_version = version;
299 288 bser->capabilities = capabilities;
300 289 if (!bser->buf) {
301 290 return 0;
302 291 }
303 292
304 293 // Leave room for the serialization header, which includes
305 294 // our overall length. To make things simpler, we'll use an
306 295 // int32 for the header
307 296 #define EMPTY_HEADER "\x00\x01\x05\x00\x00\x00\x00"
308 297
309 298 // Version 2 also carries an integer indicating the capabilities. The
310 299 // capabilities integer comes before the PDU size.
311 300 #define EMPTY_HEADER_V2 "\x00\x02\x00\x00\x00\x00\x05\x00\x00\x00\x00"
312 301 if (version == 2) {
313 302 bser_append(bser, EMPTY_HEADER_V2, sizeof(EMPTY_HEADER_V2) - 1);
314 303 } else {
315 304 bser_append(bser, EMPTY_HEADER, sizeof(EMPTY_HEADER) - 1);
316 305 }
317 306
318 307 return 1;
319 308 }
320 309
321 310 static void bser_dtor(bser_t* bser) {
322 311 free(bser->buf);
323 312 bser->buf = NULL;
324 313 }
325 314
326 315 static int bser_long(bser_t* bser, int64_t val) {
327 316 int8_t i8;
328 317 int16_t i16;
329 318 int32_t i32;
330 319 int64_t i64;
331 320 char sz;
332 321 int size = INT_SIZE(val);
333 322 char* iptr;
334 323
335 324 switch (size) {
336 325 case 1:
337 326 sz = BSER_INT8;
338 327 i8 = (int8_t)val;
339 328 iptr = (char*)&i8;
340 329 break;
341 330 case 2:
342 331 sz = BSER_INT16;
343 332 i16 = (int16_t)val;
344 333 iptr = (char*)&i16;
345 334 break;
346 335 case 4:
347 336 sz = BSER_INT32;
348 337 i32 = (int32_t)val;
349 338 iptr = (char*)&i32;
350 339 break;
351 340 case 8:
352 341 sz = BSER_INT64;
353 342 i64 = (int64_t)val;
354 343 iptr = (char*)&i64;
355 344 break;
356 345 default:
357 346 PyErr_SetString(PyExc_RuntimeError, "Cannot represent this long value!?");
358 347 return 0;
359 348 }
360 349
361 350 if (!bser_append(bser, &sz, sizeof(sz))) {
362 351 return 0;
363 352 }
364 353
365 354 return bser_append(bser, iptr, size);
366 355 }
367 356
368 357 static int bser_bytestring(bser_t* bser, PyObject* sval) {
369 358 char* buf = NULL;
370 359 Py_ssize_t len;
371 360 int res;
372 361 PyObject* utf = NULL;
373 362
374 363 if (PyUnicode_Check(sval)) {
375 364 utf = PyUnicode_AsEncodedString(sval, "utf-8", "ignore");
376 365 sval = utf;
377 366 }
378 367
379 368 res = PyBytes_AsStringAndSize(sval, &buf, &len);
380 369 if (res == -1) {
381 370 res = 0;
382 371 goto out;
383 372 }
384 373
385 374 if (!bser_append(bser, &bser_bytestring_hdr, sizeof(bser_bytestring_hdr))) {
386 375 res = 0;
387 376 goto out;
388 377 }
389 378
390 379 if (!bser_long(bser, len)) {
391 380 res = 0;
392 381 goto out;
393 382 }
394 383
395 384 if (len > UINT32_MAX) {
396 385 PyErr_Format(PyExc_ValueError, "string too big");
397 386 res = 0;
398 387 goto out;
399 388 }
400 389
401 390 res = bser_append(bser, buf, (uint32_t)len);
402 391
403 392 out:
404 393 if (utf) {
405 394 Py_DECREF(utf);
406 395 }
407 396
408 397 return res;
409 398 }
410 399
411 400 static int bser_recursive(bser_t* bser, PyObject* val) {
412 401 if (PyBool_Check(val)) {
413 402 if (val == Py_True) {
414 403 return bser_append(bser, &bser_true, sizeof(bser_true));
415 404 }
416 405 return bser_append(bser, &bser_false, sizeof(bser_false));
417 406 }
418 407
419 408 if (val == Py_None) {
420 409 return bser_append(bser, &bser_null, sizeof(bser_null));
421 410 }
422 411
423 412 // Python 3 has one integer type.
424 413 #if PY_MAJOR_VERSION < 3
425 414 if (PyInt_Check(val)) {
426 415 return bser_long(bser, PyInt_AS_LONG(val));
427 416 }
428 417 #endif // PY_MAJOR_VERSION < 3
429 418
430 419 if (PyLong_Check(val)) {
431 420 return bser_long(bser, PyLong_AsLongLong(val));
432 421 }
433 422
434 423 if (PyBytes_Check(val) || PyUnicode_Check(val)) {
435 424 return bser_bytestring(bser, val);
436 425 }
437 426
438 427 if (PyFloat_Check(val)) {
439 428 double dval = PyFloat_AS_DOUBLE(val);
440 429 char sz = BSER_REAL;
441 430
442 431 if (!bser_append(bser, &sz, sizeof(sz))) {
443 432 return 0;
444 433 }
445 434
446 435 return bser_append(bser, (char*)&dval, sizeof(dval));
447 436 }
448 437
449 438 if (PyList_Check(val)) {
450 439 Py_ssize_t i, len = PyList_GET_SIZE(val);
451 440
452 441 if (!bser_append(bser, &bser_array_hdr, sizeof(bser_array_hdr))) {
453 442 return 0;
454 443 }
455 444
456 445 if (!bser_long(bser, len)) {
457 446 return 0;
458 447 }
459 448
460 449 for (i = 0; i < len; i++) {
461 450 PyObject* ele = PyList_GET_ITEM(val, i);
462 451
463 452 if (!bser_recursive(bser, ele)) {
464 453 return 0;
465 454 }
466 455 }
467 456
468 457 return 1;
469 458 }
470 459
471 460 if (PyTuple_Check(val)) {
472 461 Py_ssize_t i, len = PyTuple_GET_SIZE(val);
473 462
474 463 if (!bser_append(bser, &bser_array_hdr, sizeof(bser_array_hdr))) {
475 464 return 0;
476 465 }
477 466
478 467 if (!bser_long(bser, len)) {
479 468 return 0;
480 469 }
481 470
482 471 for (i = 0; i < len; i++) {
483 472 PyObject* ele = PyTuple_GET_ITEM(val, i);
484 473
485 474 if (!bser_recursive(bser, ele)) {
486 475 return 0;
487 476 }
488 477 }
489 478
490 479 return 1;
491 480 }
492 481
493 482 if (PyMapping_Check(val)) {
494 483 Py_ssize_t len = PyMapping_Length(val);
495 484 Py_ssize_t pos = 0;
496 485 PyObject *key, *ele;
497 486
498 487 if (!bser_append(bser, &bser_object_hdr, sizeof(bser_object_hdr))) {
499 488 return 0;
500 489 }
501 490
502 491 if (!bser_long(bser, len)) {
503 492 return 0;
504 493 }
505 494
506 495 while (PyDict_Next(val, &pos, &key, &ele)) {
507 496 if (!bser_bytestring(bser, key)) {
508 497 return 0;
509 498 }
510 499 if (!bser_recursive(bser, ele)) {
511 500 return 0;
512 501 }
513 502 }
514 503
515 504 return 1;
516 505 }
517 506
518 507 PyErr_SetString(PyExc_ValueError, "Unsupported value type");
519 508 return 0;
520 509 }
521 510
522 511 static PyObject* bser_dumps(PyObject* self, PyObject* args, PyObject* kw) {
523 512 PyObject *val = NULL, *res;
524 513 bser_t bser;
525 514 uint32_t len, bser_version = 1, bser_capabilities = 0;
526 515
527 516 static char* kw_list[] = {"val", "version", "capabilities", NULL};
528 517
529 518 if (!PyArg_ParseTupleAndKeywords(
530 519 args,
531 520 kw,
532 521 "O|ii:dumps",
533 522 kw_list,
534 523 &val,
535 524 &bser_version,
536 525 &bser_capabilities)) {
537 526 return NULL;
538 527 }
539 528
540 529 if (!bser_init(&bser, bser_version, bser_capabilities)) {
541 530 return PyErr_NoMemory();
542 531 }
543 532
544 533 if (!bser_recursive(&bser, val)) {
545 534 bser_dtor(&bser);
546 535 if (errno == ENOMEM) {
547 536 return PyErr_NoMemory();
548 537 }
549 538 // otherwise, we've already set the error to something reasonable
550 539 return NULL;
551 540 }
552 541
553 542 // Now fill in the overall length
554 543 if (bser_version == 1) {
555 544 len = bser.wpos - (sizeof(EMPTY_HEADER) - 1);
556 545 memcpy(bser.buf + 3, &len, sizeof(len));
557 546 } else {
558 547 len = bser.wpos - (sizeof(EMPTY_HEADER_V2) - 1);
559 548 // The BSER capabilities block comes before the PDU length
560 549 memcpy(bser.buf + 2, &bser_capabilities, sizeof(bser_capabilities));
561 550 memcpy(bser.buf + 7, &len, sizeof(len));
562 551 }
563 552
564 553 res = PyBytes_FromStringAndSize(bser.buf, bser.wpos);
565 554 bser_dtor(&bser);
566 555
567 556 return res;
568 557 }
569 558
570 559 int bunser_int(const char** ptr, const char* end, int64_t* val) {
571 560 int needed;
572 561 const char* buf = *ptr;
573 562 int8_t i8;
574 563 int16_t i16;
575 564 int32_t i32;
576 565 int64_t i64;
577 566
578 567 switch (buf[0]) {
579 568 case BSER_INT8:
580 569 needed = 2;
581 570 break;
582 571 case BSER_INT16:
583 572 needed = 3;
584 573 break;
585 574 case BSER_INT32:
586 575 needed = 5;
587 576 break;
588 577 case BSER_INT64:
589 578 needed = 9;
590 579 break;
591 580 default:
592 581 PyErr_Format(
593 582 PyExc_ValueError, "invalid bser int encoding 0x%02x", buf[0]);
594 583 return 0;
595 584 }
596 585 if (end - buf < needed) {
597 586 PyErr_SetString(PyExc_ValueError, "input buffer to small for int encoding");
598 587 return 0;
599 588 }
600 589 *ptr = buf + needed;
601 590 switch (buf[0]) {
602 591 case BSER_INT8:
603 592 memcpy(&i8, buf + 1, sizeof(i8));
604 593 *val = i8;
605 594 return 1;
606 595 case BSER_INT16:
607 596 memcpy(&i16, buf + 1, sizeof(i16));
608 597 *val = i16;
609 598 return 1;
610 599 case BSER_INT32:
611 600 memcpy(&i32, buf + 1, sizeof(i32));
612 601 *val = i32;
613 602 return 1;
614 603 case BSER_INT64:
615 604 memcpy(&i64, buf + 1, sizeof(i64));
616 605 *val = i64;
617 606 return 1;
618 607 default:
619 608 return 0;
620 609 }
621 610 }
622 611
623 612 static int bunser_bytestring(
624 613 const char** ptr,
625 614 const char* end,
626 615 const char** start,
627 616 int64_t* len) {
628 617 const char* buf = *ptr;
629 618
630 619 // skip string marker
631 620 buf++;
632 621 if (!bunser_int(&buf, end, len)) {
633 622 return 0;
634 623 }
635 624
636 625 if (buf + *len > end) {
637 626 PyErr_Format(PyExc_ValueError, "invalid string length in bser data");
638 627 return 0;
639 628 }
640 629
641 630 *ptr = buf + *len;
642 631 *start = buf;
643 632 return 1;
644 633 }
645 634
646 635 static PyObject*
647 636 bunser_array(const char** ptr, const char* end, const unser_ctx_t* ctx) {
648 637 const char* buf = *ptr;
649 638 int64_t nitems, i;
650 639 int mutable = ctx->mutable;
651 640 PyObject* res;
652 641
653 642 // skip array header
654 643 buf++;
655 644 if (!bunser_int(&buf, end, &nitems)) {
656 645 return 0;
657 646 }
658 647 *ptr = buf;
659 648
660 649 if (nitems > LONG_MAX) {
661 650 PyErr_Format(PyExc_ValueError, "too many items for python array");
662 651 return NULL;
663 652 }
664 653
665 654 if (mutable) {
666 655 res = PyList_New((Py_ssize_t)nitems);
667 656 } else {
668 657 res = PyTuple_New((Py_ssize_t)nitems);
669 658 }
670 659
671 660 for (i = 0; i < nitems; i++) {
672 661 PyObject* ele = bser_loads_recursive(ptr, end, ctx);
673 662
674 663 if (!ele) {
675 664 Py_DECREF(res);
676 665 return NULL;
677 666 }
678 667
679 668 if (mutable) {
680 669 PyList_SET_ITEM(res, i, ele);
681 670 } else {
682 671 PyTuple_SET_ITEM(res, i, ele);
683 672 }
684 673 // DECREF(ele) not required as SET_ITEM steals the ref
685 674 }
686 675
687 676 return res;
688 677 }
689 678
690 679 static PyObject*
691 680 bunser_object(const char** ptr, const char* end, const unser_ctx_t* ctx) {
692 681 const char* buf = *ptr;
693 682 int64_t nitems, i;
694 683 int mutable = ctx->mutable;
695 684 PyObject* res;
696 685 bserObject* obj;
697 686
698 687 // skip array header
699 688 buf++;
700 689 if (!bunser_int(&buf, end, &nitems)) {
701 690 return 0;
702 691 }
703 692 *ptr = buf;
704 693
705 694 if (mutable) {
706 695 res = PyDict_New();
707 696 } else {
708 697 obj = PyObject_New(bserObject, &bserObjectType);
709 698 obj->keys = PyTuple_New((Py_ssize_t)nitems);
710 699 obj->values = PyTuple_New((Py_ssize_t)nitems);
711 700 res = (PyObject*)obj;
712 701 }
713 702
714 703 for (i = 0; i < nitems; i++) {
715 704 const char* keystr;
716 705 int64_t keylen;
717 706 PyObject* key;
718 707 PyObject* ele;
719 708
720 709 if (!bunser_bytestring(ptr, end, &keystr, &keylen)) {
721 710 Py_DECREF(res);
722 711 return NULL;
723 712 }
724 713
725 714 if (keylen > LONG_MAX) {
726 715 PyErr_Format(PyExc_ValueError, "string too big for python");
727 716 Py_DECREF(res);
728 717 return NULL;
729 718 }
730 719
731 720 if (mutable) {
732 721 // This will interpret the key as UTF-8.
733 722 key = PyUnicode_FromStringAndSize(keystr, (Py_ssize_t)keylen);
734 723 } else {
735 724 // For immutable objects we'll manage key lookups, so we can avoid going
736 725 // through the Unicode APIs. This avoids a potentially expensive and
737 726 // definitely unnecessary conversion to UTF-16 and back for Python 2.
738 727 // TODO: On Python 3 the Unicode APIs are smarter: we might be able to use
739 728 // Unicode keys there without an appreciable performance loss.
740 729 key = PyBytes_FromStringAndSize(keystr, (Py_ssize_t)keylen);
741 730 }
742 731
743 732 if (!key) {
744 733 Py_DECREF(res);
745 734 return NULL;
746 735 }
747 736
748 737 ele = bser_loads_recursive(ptr, end, ctx);
749 738
750 739 if (!ele) {
751 740 Py_DECREF(key);
752 741 Py_DECREF(res);
753 742 return NULL;
754 743 }
755 744
756 745 if (mutable) {
757 746 PyDict_SetItem(res, key, ele);
758 747 Py_DECREF(key);
759 748 Py_DECREF(ele);
760 749 } else {
761 750 /* PyTuple_SET_ITEM steals ele, key */
762 751 PyTuple_SET_ITEM(obj->values, i, ele);
763 752 PyTuple_SET_ITEM(obj->keys, i, key);
764 753 }
765 754 }
766 755
767 756 return res;
768 757 }
769 758
770 759 static PyObject*
771 760 bunser_template(const char** ptr, const char* end, const unser_ctx_t* ctx) {
772 761 const char* buf = *ptr;
773 762 int64_t nitems, i;
774 763 int mutable = ctx->mutable;
775 764 PyObject* arrval;
776 765 PyObject* keys;
777 766 Py_ssize_t numkeys, keyidx;
778 767 unser_ctx_t keys_ctx = {0};
779 768 if (mutable) {
780 769 keys_ctx.mutable = 1;
781 770 // Decode keys as UTF-8 in this case.
782 771 keys_ctx.value_encoding = "utf-8";
783 772 keys_ctx.value_errors = "strict";
784 773 } else {
785 774 // Treat keys as bytestrings in this case -- we'll do Unicode conversions at
786 775 // lookup time.
787 776 }
788 777
789 778 if (buf[1] != BSER_ARRAY) {
790 779 PyErr_Format(PyExc_ValueError, "Expect ARRAY to follow TEMPLATE");
791 780 return NULL;
792 781 }
793 782
794 783 // skip header
795 784 buf++;
796 785 *ptr = buf;
797 786
798 787 // Load template keys.
799 788 // For keys we don't want to do any decoding right now.
800 789 keys = bunser_array(ptr, end, &keys_ctx);
801 790 if (!keys) {
802 791 return NULL;
803 792 }
804 793
805 794 numkeys = PySequence_Length(keys);
806 795
807 796 // Load number of array elements
808 797 if (!bunser_int(ptr, end, &nitems)) {
809 798 Py_DECREF(keys);
810 799 return 0;
811 800 }
812 801
813 802 if (nitems > LONG_MAX) {
814 803 PyErr_Format(PyExc_ValueError, "Too many items for python");
815 804 Py_DECREF(keys);
816 805 return NULL;
817 806 }
818 807
819 808 arrval = PyList_New((Py_ssize_t)nitems);
820 809 if (!arrval) {
821 810 Py_DECREF(keys);
822 811 return NULL;
823 812 }
824 813
825 814 for (i = 0; i < nitems; i++) {
826 815 PyObject* dict = NULL;
827 816 bserObject* obj = NULL;
828 817
829 818 if (mutable) {
830 819 dict = PyDict_New();
831 820 } else {
832 821 obj = PyObject_New(bserObject, &bserObjectType);
833 822 if (obj) {
834 823 obj->keys = keys;
835 824 Py_INCREF(obj->keys);
836 825 obj->values = PyTuple_New(numkeys);
837 826 }
838 827 dict = (PyObject*)obj;
839 828 }
840 829 if (!dict) {
841 830 fail:
842 831 Py_DECREF(keys);
843 832 Py_DECREF(arrval);
844 833 return NULL;
845 834 }
846 835
847 836 for (keyidx = 0; keyidx < numkeys; keyidx++) {
848 837 PyObject* key;
849 838 PyObject* ele;
850 839
851 840 if (**ptr == BSER_SKIP) {
852 841 *ptr = *ptr + 1;
853 842 ele = Py_None;
854 843 Py_INCREF(ele);
855 844 } else {
856 845 ele = bser_loads_recursive(ptr, end, ctx);
857 846 }
858 847
859 848 if (!ele) {
860 849 goto fail;
861 850 }
862 851
863 852 if (mutable) {
864 853 key = PyList_GET_ITEM(keys, keyidx);
865 854 PyDict_SetItem(dict, key, ele);
866 855 Py_DECREF(ele);
867 856 } else {
868 857 PyTuple_SET_ITEM(obj->values, keyidx, ele);
869 858 // DECREF(ele) not required as SET_ITEM steals the ref
870 859 }
871 860 }
872 861
873 862 PyList_SET_ITEM(arrval, i, dict);
874 863 // DECREF(obj) not required as SET_ITEM steals the ref
875 864 }
876 865
877 866 Py_DECREF(keys);
878 867
879 868 return arrval;
880 869 }
881 870
882 871 static PyObject* bser_loads_recursive(
883 872 const char** ptr,
884 873 const char* end,
885 874 const unser_ctx_t* ctx) {
886 875 const char* buf = *ptr;
887 876
888 877 switch (buf[0]) {
889 878 case BSER_INT8:
890 879 case BSER_INT16:
891 880 case BSER_INT32:
892 881 case BSER_INT64: {
893 882 int64_t ival;
894 883 if (!bunser_int(ptr, end, &ival)) {
895 884 return NULL;
896 885 }
897 886 // Python 3 has one integer type.
898 887 #if PY_MAJOR_VERSION >= 3
899 888 return PyLong_FromLongLong(ival);
900 889 #else
901 890 if (ival < LONG_MIN || ival > LONG_MAX) {
902 891 return PyLong_FromLongLong(ival);
903 892 }
904 893 return PyInt_FromSsize_t(Py_SAFE_DOWNCAST(ival, int64_t, Py_ssize_t));
905 894 #endif // PY_MAJOR_VERSION >= 3
906 895 }
907 896
908 897 case BSER_REAL: {
909 898 double dval;
910 899 memcpy(&dval, buf + 1, sizeof(dval));
911 900 *ptr = buf + 1 + sizeof(double);
912 901 return PyFloat_FromDouble(dval);
913 902 }
914 903
915 904 case BSER_TRUE:
916 905 *ptr = buf + 1;
917 906 Py_INCREF(Py_True);
918 907 return Py_True;
919 908
920 909 case BSER_FALSE:
921 910 *ptr = buf + 1;
922 911 Py_INCREF(Py_False);
923 912 return Py_False;
924 913
925 914 case BSER_NULL:
926 915 *ptr = buf + 1;
927 916 Py_INCREF(Py_None);
928 917 return Py_None;
929 918
930 919 case BSER_BYTESTRING: {
931 920 const char* start;
932 921 int64_t len;
933 922
934 923 if (!bunser_bytestring(ptr, end, &start, &len)) {
935 924 return NULL;
936 925 }
937 926
938 927 if (len > LONG_MAX) {
939 928 PyErr_Format(PyExc_ValueError, "string too long for python");
940 929 return NULL;
941 930 }
942 931
943 932 if (ctx->value_encoding != NULL) {
944 933 return PyUnicode_Decode(
945 934 start, (long)len, ctx->value_encoding, ctx->value_errors);
946 935 } else {
947 936 return PyBytes_FromStringAndSize(start, (long)len);
948 937 }
949 938 }
950 939
951 940 case BSER_UTF8STRING: {
952 941 const char* start;
953 942 int64_t len;
954 943
955 944 if (!bunser_bytestring(ptr, end, &start, &len)) {
956 945 return NULL;
957 946 }
958 947
959 948 if (len > LONG_MAX) {
960 949 PyErr_Format(PyExc_ValueError, "string too long for python");
961 950 return NULL;
962 951 }
963 952
964 953 return PyUnicode_Decode(start, (long)len, "utf-8", "strict");
965 954 }
966 955
967 956 case BSER_ARRAY:
968 957 return bunser_array(ptr, end, ctx);
969 958
970 959 case BSER_OBJECT:
971 960 return bunser_object(ptr, end, ctx);
972 961
973 962 case BSER_TEMPLATE:
974 963 return bunser_template(ptr, end, ctx);
975 964
976 965 default:
977 966 PyErr_Format(PyExc_ValueError, "unhandled bser opcode 0x%02x", buf[0]);
978 967 }
979 968
980 969 return NULL;
981 970 }
982 971
983 972 static int _pdu_info_helper(
984 973 const char* data,
985 974 const char* end,
986 975 uint32_t* bser_version_out,
987 976 uint32_t* bser_capabilities_out,
988 977 int64_t* expected_len_out,
989 978 off_t* position_out) {
990 979 uint32_t bser_version;
991 980 uint32_t bser_capabilities = 0;
992 981 int64_t expected_len;
993 982
994 983 const char* start;
995 984 start = data;
996 985 // Validate the header and length
997 986 if (memcmp(data, EMPTY_HEADER, 2) == 0) {
998 987 bser_version = 1;
999 988 } else if (memcmp(data, EMPTY_HEADER_V2, 2) == 0) {
1000 989 bser_version = 2;
1001 990 } else {
1002 991 PyErr_SetString(PyExc_ValueError, "invalid bser header");
1003 992 return 0;
1004 993 }
1005 994
1006 995 data += 2;
1007 996
1008 997 if (bser_version == 2) {
1009 998 // Expect an integer telling us what capabilities are supported by the
1010 999 // remote server (currently unused).
1011 1000 if (!memcpy(&bser_capabilities, &data, sizeof(bser_capabilities))) {
1012 1001 return 0;
1013 1002 }
1014 1003 data += sizeof(bser_capabilities);
1015 1004 }
1016 1005
1017 1006 // Expect an integer telling us how big the rest of the data
1018 1007 // should be
1019 1008 if (!bunser_int(&data, end, &expected_len)) {
1020 1009 return 0;
1021 1010 }
1022 1011
1023 1012 *bser_version_out = bser_version;
1024 1013 *bser_capabilities_out = (uint32_t)bser_capabilities;
1025 1014 *expected_len_out = expected_len;
1026 1015 *position_out = (off_t)(data - start);
1027 1016 return 1;
1028 1017 }
1029 1018
1030 1019 // This function parses the PDU header and provides info about the packet
1031 1020 // Returns false if unsuccessful
1032 1021 static int pdu_info_helper(
1033 1022 PyObject* self,
1034 1023 PyObject* args,
1035 1024 uint32_t* bser_version_out,
1036 1025 uint32_t* bser_capabilities_out,
1037 1026 int64_t* total_len_out) {
1038 1027 const char* start = NULL;
1039 1028 const char* data = NULL;
1040 1029 int datalen = 0;
1041 1030 const char* end;
1042 1031 int64_t expected_len;
1043 1032 off_t position;
1044 1033
1045 1034 if (!PyArg_ParseTuple(args, "s#", &start, &datalen)) {
1046 1035 return 0;
1047 1036 }
1048 1037 data = start;
1049 1038 end = data + datalen;
1050 1039
1051 1040 if (!_pdu_info_helper(
1052 1041 data,
1053 1042 end,
1054 1043 bser_version_out,
1055 1044 bser_capabilities_out,
1056 1045 &expected_len,
1057 1046 &position)) {
1058 1047 return 0;
1059 1048 }
1060 1049 *total_len_out = (int64_t)(expected_len + position);
1061 1050 return 1;
1062 1051 }
1063 1052
1064 1053 // Expected use case is to read a packet from the socket and then call
1065 1054 // bser.pdu_info on the packet. It returns the BSER version, BSER capabilities,
1066 1055 // and the total length of the entire response that the peer is sending,
1067 1056 // including the bytes already received. This allows the client to compute the
1068 1057 // data size it needs to read before it can decode the data.
1069 1058 static PyObject* bser_pdu_info(PyObject* self, PyObject* args) {
1070 1059 uint32_t version, capabilities;
1071 1060 int64_t total_len;
1072 1061 if (!pdu_info_helper(self, args, &version, &capabilities, &total_len)) {
1073 1062 return NULL;
1074 1063 }
1075 1064 return Py_BuildValue("kkL", version, capabilities, total_len);
1076 1065 }
1077 1066
1078 1067 static PyObject* bser_pdu_len(PyObject* self, PyObject* args) {
1079 1068 uint32_t version, capabilities;
1080 1069 int64_t total_len;
1081 1070 if (!pdu_info_helper(self, args, &version, &capabilities, &total_len)) {
1082 1071 return NULL;
1083 1072 }
1084 1073 return Py_BuildValue("L", total_len);
1085 1074 }
1086 1075
1087 1076 static PyObject* bser_loads(PyObject* self, PyObject* args, PyObject* kw) {
1088 1077 const char* data = NULL;
1089 1078 int datalen = 0;
1090 1079 const char* start;
1091 1080 const char* end;
1092 1081 int64_t expected_len;
1093 1082 off_t position;
1094 1083 PyObject* mutable_obj = NULL;
1095 1084 const char* value_encoding = NULL;
1096 1085 const char* value_errors = NULL;
1097 1086 unser_ctx_t ctx = {1, 0};
1098 1087
1099 1088 static char* kw_list[] = {
1100 1089 "buf", "mutable", "value_encoding", "value_errors", NULL};
1101 1090
1102 1091 if (!PyArg_ParseTupleAndKeywords(
1103 1092 args,
1104 1093 kw,
1105 1094 "s#|Ozz:loads",
1106 1095 kw_list,
1107 1096 &start,
1108 1097 &datalen,
1109 1098 &mutable_obj,
1110 1099 &value_encoding,
1111 1100 &value_errors)) {
1112 1101 return NULL;
1113 1102 }
1114 1103
1115 1104 if (mutable_obj) {
1116 1105 ctx.mutable = PyObject_IsTrue(mutable_obj) > 0 ? 1 : 0;
1117 1106 }
1118 1107 ctx.value_encoding = value_encoding;
1119 1108 if (value_encoding == NULL) {
1120 1109 ctx.value_errors = NULL;
1121 1110 } else if (value_errors == NULL) {
1122 1111 ctx.value_errors = "strict";
1123 1112 } else {
1124 1113 ctx.value_errors = value_errors;
1125 1114 }
1126 1115 data = start;
1127 1116 end = data + datalen;
1128 1117
1129 1118 if (!_pdu_info_helper(
1130 1119 data,
1131 1120 end,
1132 1121 &ctx.bser_version,
1133 1122 &ctx.bser_capabilities,
1134 1123 &expected_len,
1135 1124 &position)) {
1136 1125 return NULL;
1137 1126 }
1138 1127
1139 1128 data = start + position;
1140 1129 // Verify
1141 1130 if (expected_len + data != end) {
1142 1131 PyErr_SetString(PyExc_ValueError, "bser data len != header len");
1143 1132 return NULL;
1144 1133 }
1145 1134
1146 1135 return bser_loads_recursive(&data, end, &ctx);
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};
1158 1151
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,
1166 1159 &value_encoding,
1167 1160 &value_errors)) {
1168 1161 return NULL;
1169 1162 }
1170 1163
1171 1164 load = PyImport_ImportModule("pywatchman.load");
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 }
1180 1198
1181 1199 // clang-format off
1182 1200 static PyMethodDef bser_methods[] = {
1183 1201 {"loads", (PyCFunction)bser_loads, METH_VARARGS | METH_KEYWORDS,
1184 1202 "Deserialize string."},
1185 1203 {"load", (PyCFunction)bser_load, METH_VARARGS | METH_KEYWORDS,
1186 1204 "Deserialize a file object"},
1187 1205 {"pdu_info", (PyCFunction)bser_pdu_info, METH_VARARGS,
1188 1206 "Extract PDU information."},
1189 1207 {"pdu_len", (PyCFunction)bser_pdu_len, METH_VARARGS,
1190 1208 "Extract total PDU length."},
1191 1209 {"dumps", (PyCFunction)bser_dumps, METH_VARARGS | METH_KEYWORDS,
1192 1210 "Serialize string."},
1193 1211 {NULL, NULL, 0, NULL}
1194 1212 };
1195 1213
1196 1214 #if PY_MAJOR_VERSION >= 3
1197 1215 static struct PyModuleDef bser_module = {
1198 1216 PyModuleDef_HEAD_INIT,
1199 1217 "bser",
1200 1218 "Efficient encoding and decoding of BSER.",
1201 1219 -1,
1202 1220 bser_methods
1203 1221 };
1204 1222 // clang-format on
1205 1223
1206 1224 PyMODINIT_FUNC PyInit_bser(void) {
1207 1225 PyObject* mod;
1208 1226
1209 1227 mod = PyModule_Create(&bser_module);
1210 1228 PyType_Ready(&bserObjectType);
1211 1229
1212 1230 return mod;
1213 1231 }
1214 1232 #else
1215 1233
1216 1234 PyMODINIT_FUNC initbser(void) {
1217 1235 (void)Py_InitModule("bser", bser_methods);
1218 1236 PyType_Ready(&bserObjectType);
1219 1237 }
1220 1238 #endif // PY_MAJOR_VERSION >= 3
1221 1239
1222 1240 /* vim:ts=2:sw=2:et:
1223 1241 */
@@ -1,71 +1,77 b''
1 1 # Copyright 2015 Facebook, Inc.
2 2 # All rights reserved.
3 3 #
4 4 # Redistribution and use in source and binary forms, with or without
5 5 # modification, are permitted provided that the following conditions are met:
6 6 #
7 7 # * Redistributions of source code must retain the above copyright notice,
8 8 # this list of conditions and the following disclaimer.
9 9 #
10 10 # * Redistributions in binary form must reproduce the above copyright notice,
11 11 # this list of conditions and the following disclaimer in the documentation
12 12 # and/or other materials provided with the distribution.
13 13 #
14 14 # * Neither the name Facebook nor the names of its contributors may be used to
15 15 # endorse or promote products derived from this software without specific
16 16 # prior written permission.
17 17 #
18 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 19 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
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",
46 46 "relative_root": "3.3",
47 47 "term-dirname": "3.1",
48 48 "term-idirname": "3.1",
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
@@ -1,65 +1,71 b''
1 1 # Copyright 2016-present Facebook, Inc.
2 2 # All rights reserved.
3 3 #
4 4 # Redistribution and use in source and binary forms, with or without
5 5 # modification, are permitted provided that the following conditions are met:
6 6 #
7 7 # * Redistributions of source code must retain the above copyright notice,
8 8 # this list of conditions and the following disclaimer.
9 9 #
10 10 # * Redistributions in binary form must reproduce the above copyright notice,
11 11 # this list of conditions and the following disclaimer in the documentation
12 12 # and/or other materials provided with the distribution.
13 13 #
14 14 # * Neither the name Facebook nor the names of its contributors may be used to
15 15 # endorse or promote products derived from this software without specific
16 16 # prior written permission.
17 17 #
18 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 19 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
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:
46 48 value = tp()
47 49 if value.__traceback__ is not tb:
48 50 raise value.with_traceback(tb)
49 51 raise value
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
@@ -1,73 +1,75 b''
1 1 # Copyright 2016-present Facebook, Inc.
2 2 # All rights reserved.
3 3 #
4 4 # Redistribution and use in source and binary forms, with or without
5 5 # modification, are permitted provided that the following conditions are met:
6 6 #
7 7 # * Redistributions of source code must retain the above copyright notice,
8 8 # this list of conditions and the following disclaimer.
9 9 #
10 10 # * Redistributions in binary form must reproduce the above copyright notice,
11 11 # this list of conditions and the following disclaimer in the documentation
12 12 # and/or other materials provided with the distribution.
13 13 #
14 14 # * Neither the name Facebook nor the names of its contributors may be used to
15 15 # endorse or promote products derived from this software without specific
16 16 # prior written permission.
17 17 #
18 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 19 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
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)
@@ -1,107 +1,109 b''
1 1 # Copyright 2016 Facebook, Inc.
2 2 # All rights reserved.
3 3 #
4 4 # Redistribution and use in source and binary forms, with or without
5 5 # modification, are permitted provided that the following conditions are met:
6 6 #
7 7 # * Redistributions of source code must retain the above copyright notice,
8 8 # this list of conditions and the following disclaimer.
9 9 #
10 10 # * Redistributions in binary form must reproduce the above copyright notice,
11 11 # this list of conditions and the following disclaimer in the documentation
12 12 # and/or other materials provided with the distribution.
13 13 #
14 14 # * Neither the name Facebook nor the names of its contributors may be used to
15 15 # endorse or promote products derived from this software without specific
16 16 # prior written permission.
17 17 #
18 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 19 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
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
43 43
44 44 def _read_bytes(fp, buf):
45 45 """Read bytes from a file-like object
46 46
47 47 @param fp: File-like object that implements read(int)
48 48 @type fp: file
49 49
50 50 @param buf: Buffer to read into
51 51 @type buf: bytes
52 52
53 53 @return: buf
54 54 """
55 55
56 56 # Do the first read without resizing the input buffer
57 57 offset = 0
58 58 remaining = len(buf)
59 59 while remaining > 0:
60 60 l = fp.readinto((ctypes.c_char * remaining).from_buffer(buf, offset))
61 61 if l is None or l == 0:
62 62 return offset
63 63 offset += l
64 64 remaining -= l
65 65 return offset
66 66
67 67
68 68 def load(fp, mutable=True, value_encoding=None, value_errors=None):
69 69 """Deserialize a BSER-encoded blob.
70 70
71 71 @param fp: The file-object to deserialize.
72 72 @type file:
73 73
74 74 @param mutable: Whether to return mutable results.
75 75 @type mutable: bool
76 76
77 77 @param value_encoding: Optional codec to use to decode values. If
78 78 unspecified or None, return values as bytestrings.
79 79 @type value_encoding: str
80 80
81 81 @param value_errors: Optional error handler for codec. 'strict' by default.
82 82 The other most common argument is 'surrogateescape' on
83 83 Python 3. If value_encoding is None, this is ignored.
84 84 @type value_errors: str
85 85 """
86 86 buf = ctypes.create_string_buffer(8192)
87 87 SNIFF_BUFFER_SIZE = len(EMPTY_HEADER)
88 88 header = (ctypes.c_char * SNIFF_BUFFER_SIZE).from_buffer(buf)
89 89 read_len = _read_bytes(fp, header)
90 90 if read_len < len(header):
91 91 return None
92 92
93 93 total_len = bser.pdu_len(buf)
94 94 if total_len > len(buf):
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 )
@@ -1,483 +1,576 b''
1 1 # Copyright 2015 Facebook, Inc.
2 2 # All rights reserved.
3 3 #
4 4 # Redistribution and use in source and binary forms, with or without
5 5 # modification, are permitted provided that the following conditions are met:
6 6 #
7 7 # * Redistributions of source code must retain the above copyright notice,
8 8 # this list of conditions and the following disclaimer.
9 9 #
10 10 # * Redistributions in binary form must reproduce the above copyright notice,
11 11 # this list of conditions and the following disclaimer in the documentation
12 12 # and/or other materials provided with the distribution.
13 13 #
14 14 # * Neither the name Facebook nor the names of its contributors may be used to
15 15 # endorse or promote products derived from this software without specific
16 16 # prior written permission.
17 17 #
18 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 19 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
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
36 34 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)
67 66 tobytes = bytes
68 67
69 68 # Leave room for the serialization header, which includes
70 69 # our overall length. To make things simpler, we'll use an
71 70 # int32 for the header
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:
78 78 return 1
79 79 elif -0x8000 <= x <= 0x7FFF:
80 80 return 2
81 81 elif -0x80000000 <= x <= 0x7FFFFFFF:
82 82 return 4
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):
112 117 while ctypes.sizeof(self.buf) - self.wpos < size:
113 118 ctypes.resize(self.buf, ctypes.sizeof(self.buf) * 2)
114 119
115 120 def append_long(self, val):
116 121 size = _int_size(val)
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
159 190 self.ensure_size(needed)
160 191 if val:
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)
173 204 elif isinstance(val, STRING_TYPES):
174 205 self.append_string(val)
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):
236 312 bser_buf = _bser_buffer(version=version)
237 313 bser_buf.append_recursive(obj)
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
256 333 self._values = values
257 334
258 335 def __getattr__(self, name):
259 336 return self.__getitem__(name)
260 337
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
279 357 self.value_encoding = value_encoding
280 358
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
288 366 @staticmethod
289 367 def unser_int(buf, pos):
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
323 403 return (str_val, pos + str_len)
324 404
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
337 417 def unser_object(self, buf, pos):
338 418 obj_len, pos = self.unser_int(buf, pos + 1)
339 419 if self.mutable:
340 420 obj = {}
341 421 else:
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:
349 429 obj[key] = val
350 430 else:
351 431 keys.append(key)
352 432 vals.append(val)
353 433
354 434 if not self.mutable:
355 435 obj = _BunserDict(keys, vals)
356 436
357 437 return obj, pos
358 438
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:
372 452 vals = []
373 453
374 454 for keyidx in range(len(keys)):
375 455 if _buf_pos(buf, pos) == BSER_SKIP:
376 456 pos += 1
377 457 ele = None
378 458 else:
379 459 ele, pos = self.loads_recursive(buf, pos)
380 460
381 461 if self.mutable:
382 462 key = keys[keyidx]
383 463 obj[key] = ele
384 464 else:
385 465 vals.append(ele)
386 466
387 467 if not self.mutable:
388 468 obj = _BunserDict(keys, vals)
389 469
390 470 arr.append(obj)
391 471 return arr, pos
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)
403 487 elif val_type == BSER_FALSE:
404 488 return (False, pos + 1)
405 489 elif val_type == BSER_NULL:
406 490 return (None, pos + 1)
407 491 elif val_type == BSER_BYTESTRING:
408 492 return self.unser_bytestring(buf, pos)
409 493 elif val_type == BSER_UTF8STRING:
410 494 return self.unser_utf8_string(buf, pos)
411 495 elif val_type == BSER_ARRAY:
412 496 return self.unser_array(buf, pos)
413 497 elif val_type == BSER_OBJECT:
414 498 return self.unser_object(buf, pos)
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
438 525
439 526 def pdu_info(buf):
440 527 info = _pdu_info_helper(buf)
441 528 return info[0], info[1], info[2] + info[3]
442 529
443 530
444 531 def pdu_len(buf):
445 532 info = _pdu_info_helper(buf)
446 533 return info[2] + info[3]
447 534
448 535
449 536 def loads(buf, mutable=True, value_encoding=None, value_errors=None):
450 537 """Deserialize a BSER-encoded blob.
451 538
452 539 @param buf: The buffer to deserialize.
453 540 @type buf: bytes
454 541
455 542 @param mutable: Whether to return mutable results.
456 543 @type mutable: bool
457 544
458 545 @param value_encoding: Optional codec to use to decode values. If
459 546 unspecified or None, return values as bytestrings.
460 547 @type value_encoding: str
461 548
462 549 @param value_errors: Optional error handler for codec. 'strict' by default.
463 550 The other most common argument is 'surrogateescape' on
464 551 Python 3. If value_encoding is None, this is ignored.
465 552 @type value_errors: str
466 553 """
467 554
468 555 info = _pdu_info_helper(buf)
469 556 expected_len = info[2]
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)
@@ -1,118 +1,119 b''
1 1 # watchmanclient.py - Watchman client for the fsmonitor extension
2 2 #
3 3 # Copyright 2013-2016 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
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
16 17
17 18 class Unavailable(Exception):
18 19 def __init__(self, msg, warn=True, invalidate=False):
19 20 self.msg = msg
20 21 self.warn = warn
21 22 if self.msg == b'timed out waiting for response':
22 23 self.warn = False
23 24 self.invalidate = invalidate
24 25
25 26 def __str__(self):
26 27 if self.warn:
27 28 return b'warning: Watchman unavailable: %s' % self.msg
28 29 else:
29 30 return b'Watchman unavailable: %s' % self.msg
30 31
31 32
32 33 class WatchmanNoRoot(Unavailable):
33 34 def __init__(self, root, msg):
34 35 self.root = root
35 36 super(WatchmanNoRoot, self).__init__(msg)
36 37
37 38
38 39 class client(object):
39 40 def __init__(self, ui, root, timeout=1.0):
40 41 err = None
41 42 if not self._user:
42 43 err = b"couldn't get user"
43 44 warn = True
44 45 if self._user in ui.configlist(b'fsmonitor', b'blacklistusers'):
45 46 err = b'user %s in blacklist' % self._user
46 47 warn = False
47 48
48 49 if err:
49 50 raise Unavailable(err, warn)
50 51
51 52 self._timeout = timeout
52 53 self._watchmanclient = None
53 54 self._root = root
54 55 self._ui = ui
55 56 self._firsttime = True
56 57
57 58 def settimeout(self, timeout):
58 59 self._timeout = timeout
59 60 if self._watchmanclient is not None:
60 61 self._watchmanclient.setTimeout(timeout)
61 62
62 63 def getcurrentclock(self):
63 64 result = self.command(b'clock')
64 65 if not util.safehasattr(result, 'clock'):
65 66 raise Unavailable(
66 67 b'clock result is missing clock value', invalidate=True
67 68 )
68 69 return result.clock
69 70
70 71 def clearconnection(self):
71 72 self._watchmanclient = None
72 73
73 74 def available(self):
74 75 return self._watchmanclient is not None or self._firsttime
75 76
76 77 @util.propertycache
77 78 def _user(self):
78 79 try:
79 80 return getpass.getuser()
80 81 except KeyError:
81 82 # couldn't figure out our user
82 83 return None
83 84
84 85 def _command(self, *args):
85 86 watchmanargs = (args[0], self._root) + args[1:]
86 87 try:
87 88 if self._watchmanclient is None:
88 89 self._firsttime = False
89 90 watchman_exe = self._ui.configpath(
90 91 b'fsmonitor', b'watchman_exe'
91 92 )
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:
99 100 if b'unable to resolve root' in ex.msg:
100 101 raise WatchmanNoRoot(self._root, ex.msg)
101 102 raise Unavailable(ex.msg)
102 103 except pywatchman.WatchmanError as ex:
103 104 raise Unavailable(str(ex))
104 105
105 106 def command(self, *args):
106 107 try:
107 108 try:
108 109 return self._command(*args)
109 110 except WatchmanNoRoot:
110 111 # this 'watch' command can also raise a WatchmanNoRoot if
111 112 # watchman refuses to accept this root
112 113 self._command(b'watch')
113 114 return self._command(*args)
114 115 except Unavailable:
115 116 # this is in an outer scope to catch Unavailable form any of the
116 117 # above _command calls
117 118 self._watchmanclient = None
118 119 raise
General Comments 0
You need to be logged in to leave comments. Login now