Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/numpy/_core/_internal.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

464 statements  

1""" 

2A place for internal code 

3 

4Some things are more easily handled Python. 

5 

6""" 

7import ast 

8import math 

9import re 

10import sys 

11import warnings 

12 

13from numpy import _NoValue 

14from numpy.exceptions import DTypePromotionError 

15 

16from .multiarray import StringDType, array, dtype, promote_types 

17 

18try: 

19 import ctypes 

20except ImportError: 

21 ctypes = None 

22 

23IS_PYPY = sys.implementation.name == 'pypy' 

24 

25if sys.byteorder == 'little': 

26 _nbo = '<' 

27else: 

28 _nbo = '>' 

29 

30def _makenames_list(adict, align): 

31 allfields = [] 

32 

33 for fname, obj in adict.items(): 

34 n = len(obj) 

35 if not isinstance(obj, tuple) or n not in (2, 3): 

36 raise ValueError("entry not a 2- or 3- tuple") 

37 if n > 2 and obj[2] == fname: 

38 continue 

39 num = int(obj[1]) 

40 if num < 0: 

41 raise ValueError("invalid offset.") 

42 format = dtype(obj[0], align=align) 

43 if n > 2: 

44 title = obj[2] 

45 else: 

46 title = None 

47 allfields.append((fname, format, num, title)) 

48 # sort by offsets 

49 allfields.sort(key=lambda x: x[2]) 

50 names = [x[0] for x in allfields] 

51 formats = [x[1] for x in allfields] 

52 offsets = [x[2] for x in allfields] 

53 titles = [x[3] for x in allfields] 

54 

55 return names, formats, offsets, titles 

56 

57# Called in PyArray_DescrConverter function when 

58# a dictionary without "names" and "formats" 

59# fields is used as a data-type descriptor. 

60def _usefields(adict, align): 

61 try: 

62 names = adict[-1] 

63 except KeyError: 

64 names = None 

65 if names is None: 

66 names, formats, offsets, titles = _makenames_list(adict, align) 

67 else: 

68 formats = [] 

69 offsets = [] 

70 titles = [] 

71 for name in names: 

72 res = adict[name] 

73 formats.append(res[0]) 

74 offsets.append(res[1]) 

75 if len(res) > 2: 

76 titles.append(res[2]) 

77 else: 

78 titles.append(None) 

79 

80 return dtype({"names": names, 

81 "formats": formats, 

82 "offsets": offsets, 

83 "titles": titles}, align) 

84 

85 

86# construct an array_protocol descriptor list 

87# from the fields attribute of a descriptor 

88# This calls itself recursively but should eventually hit 

89# a descriptor that has no fields and then return 

90# a simple typestring 

91 

92def _array_descr(descriptor): 

93 fields = descriptor.fields 

94 if fields is None: 

95 subdtype = descriptor.subdtype 

96 if subdtype is None: 

97 if descriptor.metadata is None: 

98 return descriptor.str 

99 else: 

100 new = descriptor.metadata.copy() 

101 if new: 

102 return (descriptor.str, new) 

103 else: 

104 return descriptor.str 

105 else: 

106 return (_array_descr(subdtype[0]), subdtype[1]) 

107 

108 names = descriptor.names 

109 ordered_fields = [fields[x] + (x,) for x in names] 

110 result = [] 

111 offset = 0 

112 for field in ordered_fields: 

113 if field[1] > offset: 

114 num = field[1] - offset 

115 result.append(('', f'|V{num}')) 

116 offset += num 

117 elif field[1] < offset: 

118 raise ValueError( 

119 "dtype.descr is not defined for types with overlapping or " 

120 "out-of-order fields") 

121 if len(field) > 3: 

122 name = (field[2], field[3]) 

123 else: 

124 name = field[2] 

125 if field[0].subdtype: 

126 tup = (name, _array_descr(field[0].subdtype[0]), 

127 field[0].subdtype[1]) 

128 else: 

129 tup = (name, _array_descr(field[0])) 

130 offset += field[0].itemsize 

131 result.append(tup) 

132 

133 if descriptor.itemsize > offset: 

134 num = descriptor.itemsize - offset 

135 result.append(('', f'|V{num}')) 

136 

137 return result 

138 

139 

140# format_re was originally from numarray by J. Todd Miller 

141 

142format_re = re.compile(r'(?P<order1>[<>|=]?)' 

143 r'(?P<repeats> *[(]?[ ,0-9]*[)]? *)' 

144 r'(?P<order2>[<>|=]?)' 

145 r'(?P<dtype>[A-Za-z0-9.?]*(?:\[[a-zA-Z0-9,.]+\])?)') 

146sep_re = re.compile(r'\s*,\s*') 

147space_re = re.compile(r'\s+$') 

148 

149# astr is a string (perhaps comma separated) 

150 

151_convorder = {'=': _nbo} 

152 

153def _commastring(astr): 

154 startindex = 0 

155 result = [] 

156 islist = False 

157 while startindex < len(astr): 

158 mo = format_re.match(astr, pos=startindex) 

159 try: 

160 (order1, repeats, order2, dtype) = mo.groups() 

161 except (TypeError, AttributeError): 

162 raise ValueError( 

163 f'format number {len(result) + 1} of "{astr}" is not recognized' 

164 ) from None 

165 startindex = mo.end() 

166 # Separator or ending padding 

167 if startindex < len(astr): 

168 if space_re.match(astr, pos=startindex): 

169 startindex = len(astr) 

170 else: 

171 mo = sep_re.match(astr, pos=startindex) 

172 if not mo: 

173 raise ValueError( 

174 'format number %d of "%s" is not recognized' % 

175 (len(result) + 1, astr)) 

176 startindex = mo.end() 

177 islist = True 

178 

179 if order2 == '': 

180 order = order1 

181 elif order1 == '': 

182 order = order2 

183 else: 

184 order1 = _convorder.get(order1, order1) 

185 order2 = _convorder.get(order2, order2) 

186 if (order1 != order2): 

187 raise ValueError( 

188 f'inconsistent byte-order specification {order1} and {order2}') 

189 order = order1 

190 

191 if order in ('|', '=', _nbo): 

192 order = '' 

193 dtype = order + dtype 

194 if repeats == '': 

195 newitem = dtype 

196 else: 

197 if (repeats[0] == "(" and repeats[-1] == ")" 

198 and repeats[1:-1].strip() != "" 

199 and "," not in repeats): 

200 warnings.warn( 

201 'Passing in a parenthesized single number for repeats ' 

202 'is deprecated; pass either a single number or indicate ' 

203 'a tuple with a comma, like "(2,)".', DeprecationWarning, 

204 stacklevel=2) 

205 newitem = (dtype, ast.literal_eval(repeats)) 

206 

207 result.append(newitem) 

208 

209 return result if islist else result[0] 

210 

211class dummy_ctype: 

212 

213 def __init__(self, cls): 

214 self._cls = cls 

215 

216 def __mul__(self, other): 

217 return self 

218 

219 def __call__(self, *other): 

220 return self._cls(other) 

221 

222 def __eq__(self, other): 

223 return self._cls == other._cls 

224 

225 def __ne__(self, other): 

226 return self._cls != other._cls 

227 

228def _getintp_ctype(): 

229 val = _getintp_ctype.cache 

230 if val is not None: 

231 return val 

232 if ctypes is None: 

233 import numpy as np 

234 val = dummy_ctype(np.intp) 

235 else: 

236 char = dtype('n').char 

237 if char == 'i': 

238 val = ctypes.c_int 

239 elif char == 'l': 

240 val = ctypes.c_long 

241 elif char == 'q': 

242 val = ctypes.c_longlong 

243 else: 

244 val = ctypes.c_long 

245 _getintp_ctype.cache = val 

246 return val 

247 

248 

249_getintp_ctype.cache = None 

250 

251# Used for .ctypes attribute of ndarray 

252 

253class _missing_ctypes: 

254 def cast(self, num, obj): 

255 return num.value 

256 

257 class c_void_p: 

258 def __init__(self, ptr): 

259 self.value = ptr 

260 

261 

262class _ctypes: 

263 def __init__(self, array, ptr=None): 

264 self._arr = array 

265 

266 if ctypes: 

267 self._ctypes = ctypes 

268 self._data = self._ctypes.c_void_p(ptr) 

269 else: 

270 # fake a pointer-like object that holds onto the reference 

271 self._ctypes = _missing_ctypes() 

272 self._data = self._ctypes.c_void_p(ptr) 

273 self._data._objects = array 

274 

275 if self._arr.ndim == 0: 

276 self._zerod = True 

277 else: 

278 self._zerod = False 

279 

280 def data_as(self, obj): 

281 """ 

282 Return the data pointer cast to a particular c-types object. 

283 For example, calling ``self._as_parameter_`` is equivalent to 

284 ``self.data_as(ctypes.c_void_p)``. Perhaps you want to use 

285 the data as a pointer to a ctypes array of floating-point data: 

286 ``self.data_as(ctypes.POINTER(ctypes.c_double))``. 

287 

288 The returned pointer will keep a reference to the array. 

289 """ 

290 # _ctypes.cast function causes a circular reference of self._data in 

291 # self._data._objects. Attributes of self._data cannot be released 

292 # until gc.collect is called. Make a copy of the pointer first then 

293 # let it hold the array reference. This is a workaround to circumvent 

294 # the CPython bug https://bugs.python.org/issue12836. 

295 ptr = self._ctypes.cast(self._data, obj) 

296 ptr._arr = self._arr 

297 return ptr 

298 

299 def shape_as(self, obj): 

300 """ 

301 Return the shape tuple as an array of some other c-types 

302 type. For example: ``self.shape_as(ctypes.c_short)``. 

303 """ 

304 if self._zerod: 

305 return None 

306 return (obj * self._arr.ndim)(*self._arr.shape) 

307 

308 def strides_as(self, obj): 

309 """ 

310 Return the strides tuple as an array of some other 

311 c-types type. For example: ``self.strides_as(ctypes.c_longlong)``. 

312 """ 

313 if self._zerod: 

314 return None 

315 return (obj * self._arr.ndim)(*self._arr.strides) 

316 

317 @property 

318 def data(self): 

319 """ 

320 A pointer to the memory area of the array as a Python integer. 

321 This memory area may contain data that is not aligned, or not in 

322 correct byte-order. The memory area may not even be writeable. 

323 The array flags and data-type of this array should be respected 

324 when passing this attribute to arbitrary C-code to avoid trouble 

325 that can include Python crashing. User Beware! The value of this 

326 attribute is exactly the same as: 

327 ``self._array_interface_['data'][0]``. 

328 

329 Note that unlike ``data_as``, a reference won't be kept to the array: 

330 code like ``ctypes.c_void_p((a + b).ctypes.data)`` will result in a 

331 pointer to a deallocated array, and should be spelt 

332 ``(a + b).ctypes.data_as(ctypes.c_void_p)`` 

333 """ 

334 return self._data.value 

335 

336 @property 

337 def shape(self): 

338 """ 

339 (c_intp*self.ndim): A ctypes array of length self.ndim where 

340 the basetype is the C-integer corresponding to ``dtype('p')`` on this 

341 platform (see `~numpy.ctypeslib.c_intp`). This base-type could be 

342 `ctypes.c_int`, `ctypes.c_long`, or `ctypes.c_longlong` depending on 

343 the platform. The ctypes array contains the shape of 

344 the underlying array. 

345 """ 

346 return self.shape_as(_getintp_ctype()) 

347 

348 @property 

349 def strides(self): 

350 """ 

351 (c_intp*self.ndim): A ctypes array of length self.ndim where 

352 the basetype is the same as for the shape attribute. This ctypes 

353 array contains the strides information from the underlying array. 

354 This strides information is important for showing how many bytes 

355 must be jumped to get to the next element in the array. 

356 """ 

357 return self.strides_as(_getintp_ctype()) 

358 

359 @property 

360 def _as_parameter_(self): 

361 """ 

362 Overrides the ctypes semi-magic method 

363 

364 Enables `c_func(some_array.ctypes)` 

365 """ 

366 return self.data_as(ctypes.c_void_p) 

367 

368 

369def _newnames(datatype, order): 

370 """ 

371 Given a datatype and an order object, return a new names tuple, with the 

372 order indicated 

373 """ 

374 oldnames = datatype.names 

375 nameslist = list(oldnames) 

376 if isinstance(order, str): 

377 order = [order] 

378 seen = set() 

379 if isinstance(order, (list, tuple)): 

380 for name in order: 

381 try: 

382 nameslist.remove(name) 

383 except ValueError: 

384 if name in seen: 

385 raise ValueError(f"duplicate field name: {name}") from None 

386 else: 

387 raise ValueError(f"unknown field name: {name}") from None 

388 seen.add(name) 

389 return tuple(list(order) + nameslist) 

390 raise ValueError(f"unsupported order value: {order}") 

391 

392def _copy_fields(ary): 

393 """Return copy of structured array with padding between fields removed. 

394 

395 Parameters 

396 ---------- 

397 ary : ndarray 

398 Structured array from which to remove padding bytes 

399 

400 Returns 

401 ------- 

402 ary_copy : ndarray 

403 Copy of ary with padding bytes removed 

404 """ 

405 dt = ary.dtype 

406 copy_dtype = {'names': dt.names, 

407 'formats': [dt.fields[name][0] for name in dt.names]} 

408 return array(ary, dtype=copy_dtype, copy=True) 

409 

410def _promote_fields(dt1, dt2): 

411 """ Perform type promotion for two structured dtypes. 

412 

413 Parameters 

414 ---------- 

415 dt1 : structured dtype 

416 First dtype. 

417 dt2 : structured dtype 

418 Second dtype. 

419 

420 Returns 

421 ------- 

422 out : dtype 

423 The promoted dtype 

424 

425 Notes 

426 ----- 

427 If one of the inputs is aligned, the result will be. The titles of 

428 both descriptors must match (point to the same field). 

429 """ 

430 # Both must be structured and have the same names in the same order 

431 if (dt1.names is None or dt2.names is None) or dt1.names != dt2.names: 

432 raise DTypePromotionError( 

433 f"field names `{dt1.names}` and `{dt2.names}` mismatch.") 

434 

435 # if both are identical, we can (maybe!) just return the same dtype. 

436 identical = dt1 is dt2 

437 new_fields = [] 

438 for name in dt1.names: 

439 field1 = dt1.fields[name] 

440 field2 = dt2.fields[name] 

441 new_descr = promote_types(field1[0], field2[0]) 

442 identical = identical and new_descr is field1[0] 

443 

444 # Check that the titles match (if given): 

445 if field1[2:] != field2[2:]: 

446 raise DTypePromotionError( 

447 f"field titles of field '{name}' mismatch") 

448 if len(field1) == 2: 

449 new_fields.append((name, new_descr)) 

450 else: 

451 new_fields.append(((field1[2], name), new_descr)) 

452 

453 res = dtype(new_fields, align=dt1.isalignedstruct or dt2.isalignedstruct) 

454 

455 # Might as well preserve identity (and metadata) if the dtype is identical 

456 # and the itemsize, offsets are also unmodified. This could probably be 

457 # sped up, but also probably just be removed entirely. 

458 if identical and res.itemsize == dt1.itemsize: 

459 for name in dt1.names: 

460 if dt1.fields[name][1] != res.fields[name][1]: 

461 return res # the dtype changed. 

462 return dt1 

463 

464 return res 

465 

466 

467def _getfield_is_safe(oldtype, newtype, offset): 

468 """ Checks safety of getfield for object arrays. 

469 

470 As in _view_is_safe, we need to check that memory containing objects is not 

471 reinterpreted as a non-object datatype and vice versa. 

472 

473 Parameters 

474 ---------- 

475 oldtype : data-type 

476 Data type of the original ndarray. 

477 newtype : data-type 

478 Data type of the field being accessed by ndarray.getfield 

479 offset : int 

480 Offset of the field being accessed by ndarray.getfield 

481 

482 Raises 

483 ------ 

484 TypeError 

485 If the field access is invalid 

486 

487 """ 

488 if newtype.hasobject or oldtype.hasobject: 

489 if offset == 0 and newtype == oldtype: 

490 return 

491 if oldtype.names is not None: 

492 for name in oldtype.names: 

493 if (oldtype.fields[name][1] == offset and 

494 oldtype.fields[name][0] == newtype): 

495 return 

496 raise TypeError("Cannot get/set field of an object array") 

497 return 

498 

499def _view_is_safe(oldtype, newtype): 

500 """ Checks safety of a view involving object arrays, for example when 

501 doing:: 

502 

503 np.zeros(10, dtype=oldtype).view(newtype) 

504 

505 Parameters 

506 ---------- 

507 oldtype : data-type 

508 Data type of original ndarray 

509 newtype : data-type 

510 Data type of the view 

511 

512 Raises 

513 ------ 

514 TypeError 

515 If the new type is incompatible with the old type. 

516 

517 """ 

518 

519 # if the types are equivalent, there is no problem. 

520 # for example: dtype((np.record, 'i4,i4')) == dtype((np.void, 'i4,i4')) 

521 if oldtype == newtype: 

522 return 

523 

524 if newtype.hasobject or oldtype.hasobject: 

525 raise TypeError("Cannot change data-type for array of references.") 

526 return 

527 

528 

529# Given a string containing a PEP 3118 format specifier, 

530# construct a NumPy dtype 

531 

532_pep3118_native_map = { 

533 '?': '?', 

534 'c': 'S1', 

535 'b': 'b', 

536 'B': 'B', 

537 'h': 'h', 

538 'H': 'H', 

539 'i': 'i', 

540 'I': 'I', 

541 'l': 'l', 

542 'L': 'L', 

543 'q': 'q', 

544 'Q': 'Q', 

545 'e': 'e', 

546 'f': 'f', 

547 'd': 'd', 

548 'g': 'g', 

549 'Zf': 'F', 

550 'Zd': 'D', 

551 'Zg': 'G', 

552 's': 'S', 

553 'w': 'U', 

554 'O': 'O', 

555 'x': 'V', # padding 

556} 

557_pep3118_native_typechars = ''.join(_pep3118_native_map.keys()) 

558 

559_pep3118_standard_map = { 

560 '?': '?', 

561 'c': 'S1', 

562 'b': 'b', 

563 'B': 'B', 

564 'h': 'i2', 

565 'H': 'u2', 

566 'i': 'i4', 

567 'I': 'u4', 

568 'l': 'i4', 

569 'L': 'u4', 

570 'q': 'i8', 

571 'Q': 'u8', 

572 'e': 'f2', 

573 'f': 'f', 

574 'd': 'd', 

575 'Zf': 'F', 

576 'Zd': 'D', 

577 's': 'S', 

578 'w': 'U', 

579 'O': 'O', 

580 'x': 'V', # padding 

581} 

582_pep3118_standard_typechars = ''.join(_pep3118_standard_map.keys()) 

583 

584_pep3118_unsupported_map = { 

585 'u': 'UCS-2 strings', 

586 '&': 'pointers', 

587 't': 'bitfields', 

588 'X': 'function pointers', 

589} 

590 

591class _Stream: 

592 def __init__(self, s): 

593 self.s = s 

594 self.byteorder = '@' 

595 

596 def advance(self, n): 

597 res = self.s[:n] 

598 self.s = self.s[n:] 

599 return res 

600 

601 def consume(self, c): 

602 if self.s[:len(c)] == c: 

603 self.advance(len(c)) 

604 return True 

605 return False 

606 

607 def consume_until(self, c): 

608 if callable(c): 

609 i = 0 

610 while i < len(self.s) and not c(self.s[i]): 

611 i = i + 1 

612 return self.advance(i) 

613 else: 

614 i = self.s.index(c) 

615 res = self.advance(i) 

616 self.advance(len(c)) 

617 return res 

618 

619 @property 

620 def next(self): 

621 return self.s[0] 

622 

623 def __bool__(self): 

624 return bool(self.s) 

625 

626 

627def _dtype_from_pep3118(spec): 

628 stream = _Stream(spec) 

629 dtype, align = __dtype_from_pep3118(stream, is_subdtype=False) 

630 return dtype 

631 

632def __dtype_from_pep3118(stream, is_subdtype): 

633 field_spec = { 

634 'names': [], 

635 'formats': [], 

636 'offsets': [], 

637 'itemsize': 0 

638 } 

639 offset = 0 

640 common_alignment = 1 

641 is_padding = False 

642 

643 # Parse spec 

644 while stream: 

645 value = None 

646 

647 # End of structure, bail out to upper level 

648 if stream.consume('}'): 

649 break 

650 

651 # Sub-arrays (1) 

652 shape = None 

653 if stream.consume('('): 

654 shape = stream.consume_until(')') 

655 shape = tuple(map(int, shape.split(','))) 

656 

657 # Byte order 

658 if stream.next in ('@', '=', '<', '>', '^', '!'): 

659 byteorder = stream.advance(1) 

660 if byteorder == '!': 

661 byteorder = '>' 

662 stream.byteorder = byteorder 

663 

664 # Byte order characters also control native vs. standard type sizes 

665 if stream.byteorder in ('@', '^'): 

666 type_map = _pep3118_native_map 

667 type_map_chars = _pep3118_native_typechars 

668 else: 

669 type_map = _pep3118_standard_map 

670 type_map_chars = _pep3118_standard_typechars 

671 

672 # Item sizes 

673 itemsize_str = stream.consume_until(lambda c: not c.isdigit()) 

674 if itemsize_str: 

675 itemsize = int(itemsize_str) 

676 else: 

677 itemsize = 1 

678 

679 # Data types 

680 is_padding = False 

681 

682 if stream.consume('T{'): 

683 value, align = __dtype_from_pep3118( 

684 stream, is_subdtype=True) 

685 elif stream.next in type_map_chars: 

686 if stream.next == 'Z': 

687 typechar = stream.advance(2) 

688 else: 

689 typechar = stream.advance(1) 

690 

691 is_padding = (typechar == 'x') 

692 dtypechar = type_map[typechar] 

693 if dtypechar in 'USV': 

694 dtypechar += '%d' % itemsize 

695 itemsize = 1 

696 numpy_byteorder = {'@': '=', '^': '='}.get( 

697 stream.byteorder, stream.byteorder) 

698 value = dtype(numpy_byteorder + dtypechar) 

699 align = value.alignment 

700 elif stream.next in _pep3118_unsupported_map: 

701 desc = _pep3118_unsupported_map[stream.next] 

702 raise NotImplementedError( 

703 f"Unrepresentable PEP 3118 data type {stream.next!r} ({desc})") 

704 else: 

705 raise ValueError( 

706 f"Unknown PEP 3118 data type specifier {stream.s!r}" 

707 ) 

708 

709 # 

710 # Native alignment may require padding 

711 # 

712 # Here we assume that the presence of a '@' character implicitly 

713 # implies that the start of the array is *already* aligned. 

714 # 

715 extra_offset = 0 

716 if stream.byteorder == '@': 

717 start_padding = (-offset) % align 

718 intra_padding = (-value.itemsize) % align 

719 

720 offset += start_padding 

721 

722 if intra_padding != 0: 

723 if itemsize > 1 or (shape is not None and _prod(shape) > 1): 

724 # Inject internal padding to the end of the sub-item 

725 value = _add_trailing_padding(value, intra_padding) 

726 else: 

727 # We can postpone the injection of internal padding, 

728 # as the item appears at most once 

729 extra_offset += intra_padding 

730 

731 # Update common alignment 

732 common_alignment = _lcm(align, common_alignment) 

733 

734 # Convert itemsize to sub-array 

735 if itemsize != 1: 

736 value = dtype((value, (itemsize,))) 

737 

738 # Sub-arrays (2) 

739 if shape is not None: 

740 value = dtype((value, shape)) 

741 

742 # Field name 

743 if stream.consume(':'): 

744 name = stream.consume_until(':') 

745 else: 

746 name = None 

747 

748 if not (is_padding and name is None): 

749 if name is not None and name in field_spec['names']: 

750 raise RuntimeError( 

751 f"Duplicate field name '{name}' in PEP3118 format" 

752 ) 

753 field_spec['names'].append(name) 

754 field_spec['formats'].append(value) 

755 field_spec['offsets'].append(offset) 

756 

757 offset += value.itemsize 

758 offset += extra_offset 

759 

760 field_spec['itemsize'] = offset 

761 

762 # extra final padding for aligned types 

763 if stream.byteorder == '@': 

764 field_spec['itemsize'] += (-offset) % common_alignment 

765 

766 # Check if this was a simple 1-item type, and unwrap it 

767 if (field_spec['names'] == [None] 

768 and field_spec['offsets'][0] == 0 

769 and field_spec['itemsize'] == field_spec['formats'][0].itemsize 

770 and not is_subdtype): 

771 ret = field_spec['formats'][0] 

772 else: 

773 _fix_names(field_spec) 

774 ret = dtype(field_spec) 

775 

776 # Finished 

777 return ret, common_alignment 

778 

779def _fix_names(field_spec): 

780 """ Replace names which are None with the next unused f%d name """ 

781 names = field_spec['names'] 

782 for i, name in enumerate(names): 

783 if name is not None: 

784 continue 

785 

786 j = 0 

787 while True: 

788 name = f'f{j}' 

789 if name not in names: 

790 break 

791 j = j + 1 

792 names[i] = name 

793 

794def _add_trailing_padding(value, padding): 

795 """Inject the specified number of padding bytes at the end of a dtype""" 

796 if value.fields is None: 

797 field_spec = { 

798 'names': ['f0'], 

799 'formats': [value], 

800 'offsets': [0], 

801 'itemsize': value.itemsize 

802 } 

803 else: 

804 fields = value.fields 

805 names = value.names 

806 field_spec = { 

807 'names': names, 

808 'formats': [fields[name][0] for name in names], 

809 'offsets': [fields[name][1] for name in names], 

810 'itemsize': value.itemsize 

811 } 

812 

813 field_spec['itemsize'] += padding 

814 return dtype(field_spec) 

815 

816def _prod(a): 

817 p = 1 

818 for x in a: 

819 p *= x 

820 return p 

821 

822def _gcd(a, b): 

823 """Calculate the greatest common divisor of a and b""" 

824 if not (math.isfinite(a) and math.isfinite(b)): 

825 raise ValueError('Can only find greatest common divisor of ' 

826 f'finite arguments, found "{a}" and "{b}"') 

827 while b: 

828 a, b = b, a % b 

829 return a 

830 

831def _lcm(a, b): 

832 return a // _gcd(a, b) * b 

833 

834def array_ufunc_errmsg_formatter(dummy, ufunc, method, *inputs, **kwargs): 

835 """ Format the error message for when __array_ufunc__ gives up. """ 

836 args_string = ', '.join([f'{arg!r}' for arg in inputs] + 

837 [f'{k}={v!r}' 

838 for k, v in kwargs.items()]) 

839 args = inputs + kwargs.get('out', ()) 

840 types_string = ', '.join(repr(type(arg).__name__) for arg in args) 

841 return ('operand type(s) all returned NotImplemented from ' 

842 f'__array_ufunc__({ufunc!r}, {method!r}, {args_string}): {types_string}' 

843 ) 

844 

845 

846def array_function_errmsg_formatter(public_api, types): 

847 """ Format the error message for when __array_ufunc__ gives up. """ 

848 func_name = f'{public_api.__module__}.{public_api.__name__}' 

849 return (f"no implementation found for '{func_name}' on types that implement " 

850 f'__array_function__: {list(types)}') 

851 

852 

853def _ufunc_doc_signature_formatter(ufunc): 

854 """ 

855 Builds a signature string which resembles PEP 457 

856 

857 This is used to construct the first line of the docstring 

858 

859 Keep in sync with `_ufunc_inspect_signature_builder`. 

860 """ 

861 

862 # input arguments are simple 

863 if ufunc.nin == 1: 

864 in_args = 'x' 

865 else: 

866 in_args = ', '.join(f'x{i + 1}' for i in range(ufunc.nin)) 

867 

868 # output arguments are both keyword or positional 

869 if ufunc.nout == 0: 

870 out_args = ', /, out=()' 

871 elif ufunc.nout == 1: 

872 out_args = ', /, out=None' 

873 else: 

874 out_args = '[, {positional}], / [, out={default}]'.format( 

875 positional=', '.join( 

876 f'out{i + 1}' for i in range(ufunc.nout)), 

877 default=repr((None,) * ufunc.nout) 

878 ) 

879 

880 # keyword only args depend on whether this is a gufunc 

881 kwargs = ( 

882 ", casting='same_kind'" 

883 ", order='K'" 

884 ", dtype=None" 

885 ", subok=True" 

886 ) 

887 

888 # NOTE: gufuncs may or may not support the `axis` parameter 

889 if ufunc.signature is None: 

890 kwargs = f", where=True{kwargs}[, signature]" 

891 else: 

892 kwargs += "[, signature, axes, axis]" 

893 

894 # join all the parts together 

895 return f'{ufunc.__name__}({in_args}{out_args}, *{kwargs})' 

896 

897 

898def _ufunc_inspect_signature_builder(ufunc): 

899 """ 

900 Builds a ``__signature__`` string. 

901 

902 Should be kept in sync with `_ufunc_doc_signature_formatter`. 

903 """ 

904 

905 from inspect import Parameter, Signature 

906 

907 params = [] 

908 

909 # positional-only input parameters 

910 if ufunc.nin == 1: 

911 params.append(Parameter("x", Parameter.POSITIONAL_ONLY)) 

912 else: 

913 params.extend( 

914 Parameter(f"x{i}", Parameter.POSITIONAL_ONLY) 

915 for i in range(1, ufunc.nin + 1) 

916 ) 

917 

918 # for the sake of simplicity, we only consider a single output parameter 

919 if ufunc.nout == 1: 

920 out_default = None 

921 else: 

922 out_default = (None,) * ufunc.nout 

923 params.append( 

924 Parameter("out", Parameter.POSITIONAL_OR_KEYWORD, default=out_default), 

925 ) 

926 

927 if ufunc.signature is None: 

928 params.append(Parameter("where", Parameter.KEYWORD_ONLY, default=True)) 

929 else: 

930 # NOTE: not all gufuncs support the `axis` parameters 

931 params.append(Parameter("axes", Parameter.KEYWORD_ONLY, default=_NoValue)) 

932 params.append(Parameter("axis", Parameter.KEYWORD_ONLY, default=_NoValue)) 

933 params.append(Parameter("keepdims", Parameter.KEYWORD_ONLY, default=False)) 

934 

935 params.extend(( 

936 Parameter("casting", Parameter.KEYWORD_ONLY, default='same_kind'), 

937 Parameter("order", Parameter.KEYWORD_ONLY, default='K'), 

938 Parameter("dtype", Parameter.KEYWORD_ONLY, default=None), 

939 Parameter("subok", Parameter.KEYWORD_ONLY, default=True), 

940 Parameter("signature", Parameter.KEYWORD_ONLY, default=None), 

941 )) 

942 

943 return Signature(params) 

944 

945 

946def npy_ctypes_check(cls): 

947 # determine if a class comes from ctypes, in order to work around 

948 # a bug in the buffer protocol for those objects, bpo-10746 

949 try: 

950 # ctypes class are new-style, so have an __mro__. This probably fails 

951 # for ctypes classes with multiple inheritance. 

952 if IS_PYPY: 

953 # (..., _ctypes.basics._CData, Bufferable, object) 

954 ctype_base = cls.__mro__[-3] 

955 else: 

956 # # (..., _ctypes._CData, object) 

957 ctype_base = cls.__mro__[-2] 

958 # right now, they're part of the _ctypes module 

959 return '_ctypes' in ctype_base.__module__ 

960 except Exception: 

961 return False 

962 

963# used to handle the _NoValue default argument for na_object 

964# in the C implementation of the __reduce__ method for stringdtype 

965def _convert_to_stringdtype_kwargs(coerce, na_object=_NoValue): 

966 if na_object is _NoValue: 

967 return StringDType(coerce=coerce) 

968 return StringDType(coerce=coerce, na_object=na_object)