this repo has no description
at trunk 464 lines 13 kB view raw
1#!/usr/bin/env python3 2# Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 3# $builtin-init-module$ 4"""ctypes support module""" 5 6from builtins import _obj_as_int 7from weakref import ref 8 9from _builtins import ( 10 _builtin, 11 _bytes_check, 12 _bytes_len, 13 _float_check, 14 _int_check, 15 _memoryview_check, 16 _memoryview_start, 17 _mmap_check, 18 _property, 19 _str_check, 20 _str_len, 21 _tuple_check, 22 _tuple_getitem, 23 _tuple_len, 24 _type, 25 _type_check, 26 _type_issubclass, 27 _Unbound, 28 _unimplemented, 29 _weakref_check, 30) 31 32 33_SIMPLE_TYPE_CHARS = "cbBhHiIlLdfuzZqQPXOv?g" 34 35FUNCFLAG_STDCALL = 0x0 36FUNCFLAG_CDECL = 0x1 37FUNCFLAG_HRESULT = 0x2 38FUNCFLAG_PYTHONAPI = 0x4 39FUNCFLAG_USE_ERRNO = 0x8 40FUNCFLAG_USE_LASTERROR = 0x10 41 42 43__version__ = "1.1.0" 44 45 46def _SimpleCData_value_to_type(value, typ, offset): 47 _builtin() 48 49 50# Metaclasses 51 52# Note: CPython does not define _CDataType as a metaclass, but rather as a 53# set of methods which are added to the other metaclasses. 54class _CDataType(type): 55 def from_address(cls, *args, **kwargs): 56 _unimplemented() 57 58 def from_buffer(cls, obj, offset=0): 59 if not hasattr(cls, "_type_"): 60 raise TypeError("abstract class") 61 if not _type_issubclass(cls, _SimpleCData) and not _type_issubclass(cls, Array): 62 _unimplemented() 63 if not _memoryview_check(obj) or not _mmap_check(obj.obj): 64 _unimplemented() 65 if offset < 0: 66 raise ValueError("offset cannot be negative") 67 if obj.readonly: 68 raise TypeError("underlying buffer is not writable") 69 if obj.strides != (1,): 70 raise TypeError("underlying buffer is not C contiguous") 71 obj_len = obj.shape[0] 72 type_size = sizeof(cls) 73 if type_size > obj_len - offset: 74 raise ValueError( 75 "Buffer size too small " 76 f"({obj_len} instead of at least {type_size + offset} bytes)" 77 ) 78 result = cls.__new__(cls) 79 result._value = obj.obj 80 result._offset = offset + _memoryview_start(obj) 81 return result 82 83 def from_buffer_copy(cls, *args, **kwargs): 84 _unimplemented() 85 86 def from_param(cls, *args, **kwargs): 87 _unimplemented() 88 89 def in_dll(cls, *args, **kwargs): 90 _unimplemented() 91 92 def __mul__(cls, length): 93 if length < 0: 94 raise ValueError(f"Array length must be >= 0, not {length}") 95 if not isinstance(cls, type): 96 raise TypeError(f"Expected a type object") 97 98 key = (cls, length) 99 result = _array_from_ctype_cache.get(key) 100 if result is not None: 101 return result() if _weakref_check(result) else result 102 103 name = f"{cls.__name__}_Array_{length}" 104 result = PyCArrayType(name, (Array,), {"_length_": length, "_type_": cls}) 105 _array_from_ctype_cache[key] = ref( 106 result, lambda _: _array_from_ctype_cache.pop(key) 107 ) 108 return result 109 110 111def _CharArray_value_to_bytes(obj, le): 112 _builtin() 113 114 115def _CharArray_value_getter(self): 116 return _CharArray_value_to_bytes(self._value, self._length_) 117 118 119def _CharArray_value_setter(self, value): 120 if not _bytes_check(value): 121 raise TypeError(f"bytes expected instead of {_type(value).__name__} instance") 122 val_len = _bytes_len(value) 123 if val_len > self._length_: 124 raise ValueError("byte string too long") 125 if _mmap_check(self._value): 126 memoryview(self._value)[:val_len] = value 127 else: 128 self._value[:val_len] = value 129 if val_len < self._length_: 130 self._value[val_len] = 0 131 132 133class PyCArrayType(_CDataType): 134 def __new__(cls, name, bases, namespace): 135 result = super().__new__(cls, name, bases, namespace) 136 # The base class (Array), no checking necessary 137 if bases[0] == _CData: 138 return result 139 140 if not hasattr(result, "_length_"): 141 raise AttributeError("class must define a '_length_' attribute") 142 length = result._length_ 143 if not _int_check(length): 144 raise TypeError("The '_length_' attribute must be an integer") 145 if length < 0: 146 raise ValueError("The '_length_' attribute must not be negative") 147 148 if not hasattr(result, "_type_"): 149 raise AttributeError("class must define a '_type_' attribute") 150 result_type = result._type_ 151 if not _type_check(result_type) or not hasattr(result_type, "_type_"): 152 raise TypeError("_type_ must have storage info") 153 if result_type._type_ == "c": 154 result.value = _property(_CharArray_value_getter, _CharArray_value_setter) 155 156 return result 157 158 159class PyCFuncPtrType(_CDataType): 160 pass 161 162 163class PyCPointerType(_CDataType): 164 def from_param(cls, *args, **kwargs): # noqa: B902 165 _unimplemented() 166 167 def set_type(cls, *args, **kwargs): # noqa: B902 168 _unimplemented() 169 170 171class PyCSimpleType(_CDataType): 172 def __new__(cls, name, bases, namespace): 173 result = super().__new__(cls, name, bases, namespace) 174 175 # The base class (_SimpleCData), no checking necessary 176 if bases[0] == _CData: 177 return result 178 179 if not hasattr(result, "_type_"): 180 raise AttributeError("class must define a '_type_' attribute") 181 182 proto = result._type_ 183 if not _str_check(proto): 184 raise TypeError("class must define a '_type_' string attribute") 185 186 if _str_len(proto) != 1: 187 raise ValueError( 188 "class must define a '_type_' attribute which must be a string \ 189of length 1" 190 ) 191 192 if proto not in _SIMPLE_TYPE_CHARS: 193 raise AttributeError( 194 "class must define a '_type_' attribute which must be\n" 195 + f"a single character string containing one of '{_SIMPLE_TYPE_CHARS}'." 196 ) 197 198 # TODO(T61319024): CPython does more work here 199 200 return result 201 202 def from_param(cls, *args, **kwargs): # noqa: B902 203 _unimplemented() 204 205 206class PyCStructType(_CDataType): 207 pass 208 209 210class UnionType(_CDataType): 211 pass 212 213 214# End Metaclasses 215 216 217class _CData: 218 def __ctypes_from_outparam__(self): 219 _unimplemented() 220 221 def __reduce__(self): 222 _unimplemented() 223 224 def __setstate__(self): 225 _unimplemented() 226 227 228class _Pointer(_CData, metaclass=PyCPointerType): 229 def __new__(cls, name, bases, namespace): 230 _unimplemented() 231 232 233class _SimpleCData(_CData, metaclass=PyCSimpleType): 234 def __init__(self, value=_Unbound): 235 self._value_setter(value) 236 self._offset = 0 237 238 def __new__(cls, *args, **kwargs): 239 if not hasattr(cls, "_type_"): 240 raise TypeError("abstract class") 241 return super().__new__(cls, args, kwargs) 242 243 def _value_getter(self): 244 return _SimpleCData_value_to_type(self._value, self._type_, self._offset) 245 246 def _value_setter(self, value): 247 if self._type_ == "H" or self._type_ == "L": 248 if _float_check(value): 249 raise TypeError("int expected instead of float") 250 if value is _Unbound: 251 self._value = 0 252 else: 253 self._value = _obj_as_int(value) 254 else: 255 _unimplemented() 256 257 value = _property(_value_getter, _value_setter) 258 259 260class ArgumentError(Exception): 261 pass 262 263 264class Array(_CData, metaclass=PyCArrayType): 265 def __init__(self, *args, **kwargs): 266 self._offset = 0 267 if _tuple_len(args) > 1: 268 _unimplemented() 269 elif _tuple_len(args) == 1: 270 if self._type_._type_ != "c": 271 _unimplemented() 272 arg0 = _tuple_getitem(args, 0) 273 if not _bytes_check(arg0) or _bytes_len(arg0) != 1: 274 _unimplemented() 275 self.value = arg0 276 277 def __len__(self): 278 _unimplemented() 279 280 def __getitem__(self): 281 _unimplemented() 282 283 def __new__(cls, *args, **kwargs): 284 if not hasattr(cls, "_type_"): 285 raise TypeError("abstract class") 286 result = super().__new__(cls, args, kwargs) 287 result._value = bytearray(bytes(cls._length_)) 288 return result 289 290 def __setitem__(self): 291 _unimplemented() 292 293 def __delitem__(self): 294 _unimplemented() 295 296 297# TODO(T66886328): Delete the memset patch once __call__ is complete 298def _memset(addr, val, size): 299 _builtin() 300 301 302class CFuncPtr(_CData, metaclass=PyCFuncPtrType): 303 def __call__(self, *args, **kwargs): 304 # TODO(T66886328): Delete the memset patch once __call__ is complete 305 if self.fn_pointer == _memset_addr: # noqa: F821 306 if _tuple_len(args) != 3: 307 raise TypeError("this function takes at least 3 arguments") 308 addr = _tuple_getitem(args, 0) 309 val = _tuple_getitem(args, 1) 310 size = _tuple_getitem(args, 2) 311 if not _int_check(addr) or not _int_check(val) or not _int_check(size): 312 _unimplemented() 313 return _memset(addr, val, size) 314 if _tuple_len(args) != 0: 315 _unimplemented() 316 if hasattr(self, "callable") and self.callable is not None: 317 _unimplemented() 318 return _call_cfuncptr(self.fn_pointer, self.restype._type_) 319 320 def __new__(cls, *args, **kwargs): 321 if _tuple_len(args) != 1: 322 _unimplemented() 323 324 arg = args[0] 325 if _int_check(arg): 326 result = super().__new__(cls) 327 result.fn_pointer = arg 328 elif callable(arg): 329 result = super().__new__(cls) 330 result.callable = arg 331 elif _tuple_check(arg): 332 name = arg[0] 333 if _bytes_check(name): 334 _unimplemented() 335 if not _str_check(name): 336 # Note: integer argument only supported on WIN32 337 raise TypeError("function name must be string, bytes object or integer") 338 dll = arg[1] 339 handle = dll._handle 340 if not _int_check(handle): 341 raise TypeError( 342 "the _handle attribute of the second argument must be an integer" 343 ) 344 result = super().__new__(cls) 345 result.fn_pointer = _shared_object_symbol_address(handle, name) 346 else: 347 raise TypeError("argument must be callable or integer function address") 348 349 result.restype = cls._restype_ 350 return result 351 352 353class Structure(_CData, metaclass=PyCStructType): 354 def __init__(self, *args, **kwargs): 355 _unimplemented() 356 357 def __new__(cls, *args, **kwargs): 358 _unimplemented() 359 360 361class Union(_CData, metaclass=UnionType): 362 def __init__(self, *args, **kwargs): 363 _unimplemented() 364 365 def __new__(cls, *args, **kwargs): 366 _unimplemented() 367 368 369def _addressof(obj): 370 _builtin() 371 372 373_array_from_ctype_cache = {} 374 375 376_pointer_type_cache = {} 377 378 379def _call_cfuncptr(function_ptr, result_type): 380 _builtin() 381 382 383def _sizeof_typeclass(cdata_type): 384 _builtin() 385 386 387def _shared_object_symbol_address(handle, name): 388 _builtin() 389 390 391def addressof(obj): 392 if not issubclass(type(obj), _CData): 393 raise TypeError("invalid type") 394 # TODO(T67017526): Remove mmap patch 395 # addressof can only be called on objects which are not allocated on Pyro's 396 # internal heap (such as an mmap). Objects on Pyro's heap do not have stable 397 # addresses due to the relocating garbage collector, so we should not make them 398 # visible to the user 399 if ( 400 not _type_issubclass(_type(obj), _SimpleCData) 401 and not _type_issubclass(_type(obj), Array) 402 ) or not _mmap_check(obj._value): 403 _unimplemented() 404 return _addressof(obj._value) 405 406 407def alignment(obj_or_type): 408 _unimplemented() 409 410 411def byref(obj, offset=0): 412 _unimplemented() 413 414 415def dlopen(name, flag): 416 _builtin() 417 418 419def get_errno(): 420 _unimplemented() 421 422 423def POINTER(cls): 424 result = _pointer_type_cache.get(cls) 425 if result is not None: 426 return result 427 428 if isinstance(cls, str): 429 result = PyCPointerType("LP_" + cls, (_Pointer,), {}) 430 key = id(result) 431 elif isinstance(cls, type): 432 result = PyCPointerType("LP_" + cls.__name__, (_Pointer,), {"_type_": cls}) 433 key = cls 434 else: 435 raise TypeError("must be a ctypes type") 436 437 _pointer_type_cache[key] = result 438 return result 439 440 441def pointer(obj): 442 _unimplemented() 443 444 445def resize(obj, size): 446 _unimplemented() 447 448 449def set_errno(value): 450 _unimplemented() 451 452 453def sizeof(obj_or_type): 454 if not _type_check(obj_or_type): 455 ty = _type(obj_or_type) 456 else: 457 ty = obj_or_type 458 if not _type_issubclass(ty, _CData): 459 raise TypeError("this type has no size") 460 if _type_issubclass(ty, Array): 461 return ty._length_ * sizeof(ty._type_) 462 if _type_issubclass(ty, _SimpleCData): 463 return _sizeof_typeclass(ty._type_) 464 _unimplemented()