Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/numpy/core/_dtype_ctypes.py: 34%

54 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2Conversion from ctypes to dtype. 

3 

4In an ideal world, we could achieve this through the PEP3118 buffer protocol, 

5something like:: 

6 

7 def dtype_from_ctypes_type(t): 

8 # needed to ensure that the shape of `t` is within memoryview.format 

9 class DummyStruct(ctypes.Structure): 

10 _fields_ = [('a', t)] 

11 

12 # empty to avoid memory allocation 

13 ctype_0 = (DummyStruct * 0)() 

14 mv = memoryview(ctype_0) 

15 

16 # convert the struct, and slice back out the field 

17 return _dtype_from_pep3118(mv.format)['a'] 

18 

19Unfortunately, this fails because: 

20 

21* ctypes cannot handle length-0 arrays with PEP3118 (bpo-32782) 

22* PEP3118 cannot represent unions, but both numpy and ctypes can 

23* ctypes cannot handle big-endian structs with PEP3118 (bpo-32780) 

24""" 

25 

26# We delay-import ctypes for distributions that do not include it. 

27# While this module is not used unless the user passes in ctypes 

28# members, it is eagerly imported from numpy/core/__init__.py. 

29import numpy as np 

30 

31 

32def _from_ctypes_array(t): 

33 return np.dtype((dtype_from_ctypes_type(t._type_), (t._length_,))) 

34 

35 

36def _from_ctypes_structure(t): 

37 for item in t._fields_: 

38 if len(item) > 2: 

39 raise TypeError( 

40 "ctypes bitfields have no dtype equivalent") 

41 

42 if hasattr(t, "_pack_"): 

43 import ctypes 

44 formats = [] 

45 offsets = [] 

46 names = [] 

47 current_offset = 0 

48 for fname, ftyp in t._fields_: 

49 names.append(fname) 

50 formats.append(dtype_from_ctypes_type(ftyp)) 

51 # Each type has a default offset, this is platform dependent for some types. 

52 effective_pack = min(t._pack_, ctypes.alignment(ftyp)) 

53 current_offset = ((current_offset + effective_pack - 1) // effective_pack) * effective_pack 

54 offsets.append(current_offset) 

55 current_offset += ctypes.sizeof(ftyp) 

56 

57 return np.dtype(dict( 

58 formats=formats, 

59 offsets=offsets, 

60 names=names, 

61 itemsize=ctypes.sizeof(t))) 

62 else: 

63 fields = [] 

64 for fname, ftyp in t._fields_: 

65 fields.append((fname, dtype_from_ctypes_type(ftyp))) 

66 

67 # by default, ctypes structs are aligned 

68 return np.dtype(fields, align=True) 

69 

70 

71def _from_ctypes_scalar(t): 

72 """ 

73 Return the dtype type with endianness included if it's the case 

74 """ 

75 if getattr(t, '__ctype_be__', None) is t: 

76 return np.dtype('>' + t._type_) 

77 elif getattr(t, '__ctype_le__', None) is t: 

78 return np.dtype('<' + t._type_) 

79 else: 

80 return np.dtype(t._type_) 

81 

82 

83def _from_ctypes_union(t): 

84 import ctypes 

85 formats = [] 

86 offsets = [] 

87 names = [] 

88 for fname, ftyp in t._fields_: 

89 names.append(fname) 

90 formats.append(dtype_from_ctypes_type(ftyp)) 

91 offsets.append(0) # Union fields are offset to 0 

92 

93 return np.dtype(dict( 

94 formats=formats, 

95 offsets=offsets, 

96 names=names, 

97 itemsize=ctypes.sizeof(t))) 

98 

99 

100def dtype_from_ctypes_type(t): 

101 """ 

102 Construct a dtype object from a ctypes type 

103 """ 

104 import _ctypes 

105 if issubclass(t, _ctypes.Array): 105 ↛ 106line 105 didn't jump to line 106, because the condition on line 105 was never true

106 return _from_ctypes_array(t) 

107 elif issubclass(t, _ctypes._Pointer): 107 ↛ 108line 107 didn't jump to line 108, because the condition on line 107 was never true

108 raise TypeError("ctypes pointers have no dtype equivalent") 

109 elif issubclass(t, _ctypes.Structure): 109 ↛ 110line 109 didn't jump to line 110, because the condition on line 109 was never true

110 return _from_ctypes_structure(t) 

111 elif issubclass(t, _ctypes.Union): 111 ↛ 112line 111 didn't jump to line 112, because the condition on line 111 was never true

112 return _from_ctypes_union(t) 

113 elif isinstance(getattr(t, '_type_', None), str): 113 ↛ 116line 113 didn't jump to line 116, because the condition on line 113 was never false

114 return _from_ctypes_scalar(t) 

115 else: 

116 raise NotImplementedError( 

117 "Unknown ctypes type {}".format(t.__name__))