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