Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2A place for internal code 

3 

4Some things are more easily handled Python. 

5 

6""" 

7import ast 

8import re 

9import sys 

10import platform 

11 

12from .multiarray import dtype, array, ndarray 

13try: 

14 import ctypes 

15except ImportError: 

16 ctypes = None 

17 

18IS_PYPY = platform.python_implementation() == 'PyPy' 

19 

20if (sys.byteorder == 'little'): 

21 _nbo = '<' 

22else: 

23 _nbo = '>' 

24 

25def _makenames_list(adict, align): 

26 allfields = [] 

27 fnames = list(adict.keys()) 

28 for fname in fnames: 

29 obj = adict[fname] 

30 n = len(obj) 

31 if not isinstance(obj, tuple) or n not in [2, 3]: 

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

33 if (n > 2) and (obj[2] == fname): 

34 continue 

35 num = int(obj[1]) 

36 if (num < 0): 

37 raise ValueError("invalid offset.") 

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

39 if (n > 2): 

40 title = obj[2] 

41 else: 

42 title = None 

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

44 # sort by offsets 

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

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

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

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

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

50 

51 return names, formats, offsets, titles 

52 

53# Called in PyArray_DescrConverter function when 

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

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

56def _usefields(adict, align): 

57 try: 

58 names = adict[-1] 

59 except KeyError: 

60 names = None 

61 if names is None: 

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

63 else: 

64 formats = [] 

65 offsets = [] 

66 titles = [] 

67 for name in names: 

68 res = adict[name] 

69 formats.append(res[0]) 

70 offsets.append(res[1]) 

71 if (len(res) > 2): 

72 titles.append(res[2]) 

73 else: 

74 titles.append(None) 

75 

76 return dtype({"names": names, 

77 "formats": formats, 

78 "offsets": offsets, 

79 "titles": titles}, align) 

80 

81 

82# construct an array_protocol descriptor list 

83# from the fields attribute of a descriptor 

84# This calls itself recursively but should eventually hit 

85# a descriptor that has no fields and then return 

86# a simple typestring 

87 

88def _array_descr(descriptor): 

89 fields = descriptor.fields 

90 if fields is None: 

91 subdtype = descriptor.subdtype 

92 if subdtype is None: 

93 if descriptor.metadata is None: 

94 return descriptor.str 

95 else: 

96 new = descriptor.metadata.copy() 

97 if new: 

98 return (descriptor.str, new) 

99 else: 

100 return descriptor.str 

101 else: 

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

103 

104 names = descriptor.names 

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

106 result = [] 

107 offset = 0 

108 for field in ordered_fields: 

109 if field[1] > offset: 

110 num = field[1] - offset 

111 result.append(('', '|V%d' % num)) 

112 offset += num 

113 elif field[1] < offset: 

114 raise ValueError( 

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

116 "out-of-order fields") 

117 if len(field) > 3: 

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

119 else: 

120 name = field[2] 

121 if field[0].subdtype: 

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

123 field[0].subdtype[1]) 

124 else: 

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

126 offset += field[0].itemsize 

127 result.append(tup) 

128 

129 if descriptor.itemsize > offset: 

130 num = descriptor.itemsize - offset 

131 result.append(('', '|V%d' % num)) 

132 

133 return result 

134 

135# Build a new array from the information in a pickle. 

136# Note that the name numpy.core._internal._reconstruct is embedded in 

137# pickles of ndarrays made with NumPy before release 1.0 

138# so don't remove the name here, or you'll 

139# break backward compatibility. 

140def _reconstruct(subtype, shape, dtype): 

141 return ndarray.__new__(subtype, shape, dtype) 

142 

143 

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

145 

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

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

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

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

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

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

152 

153# astr is a string (perhaps comma separated) 

154 

155_convorder = {'=': _nbo} 

156 

157def _commastring(astr): 

158 startindex = 0 

159 result = [] 

160 while startindex < len(astr): 

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

162 try: 

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

164 except (TypeError, AttributeError): 

165 raise ValueError('format number %d of "%s" is not recognized' % 

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

167 startindex = mo.end() 

168 # Separator or ending padding 

169 if startindex < len(astr): 

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

171 startindex = len(astr) 

172 else: 

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

174 if not mo: 

175 raise ValueError( 

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

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

178 startindex = mo.end() 

179 

180 if order2 == '': 

181 order = order1 

182 elif order1 == '': 

183 order = order2 

184 else: 

185 order1 = _convorder.get(order1, order1) 

186 order2 = _convorder.get(order2, order2) 

187 if (order1 != order2): 

188 raise ValueError( 

189 'inconsistent byte-order specification %s and %s' % 

190 (order1, order2)) 

191 order = order1 

192 

193 if order in ['|', '=', _nbo]: 

194 order = '' 

195 dtype = order + dtype 

196 if (repeats == ''): 

197 newitem = dtype 

198 else: 

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

200 result.append(newitem) 

201 

202 return result 

203 

204class dummy_ctype: 

205 def __init__(self, cls): 

206 self._cls = cls 

207 def __mul__(self, other): 

208 return self 

209 def __call__(self, *other): 

210 return self._cls(other) 

211 def __eq__(self, other): 

212 return self._cls == other._cls 

213 def __ne__(self, other): 

214 return self._cls != other._cls 

215 

216def _getintp_ctype(): 

217 val = _getintp_ctype.cache 

218 if val is not None: 

219 return val 

220 if ctypes is None: 

221 import numpy as np 

222 val = dummy_ctype(np.intp) 

223 else: 

224 char = dtype('p').char 

225 if (char == 'i'): 

226 val = ctypes.c_int 

227 elif char == 'l': 

228 val = ctypes.c_long 

229 elif char == 'q': 

230 val = ctypes.c_longlong 

231 else: 

232 val = ctypes.c_long 

233 _getintp_ctype.cache = val 

234 return val 

235_getintp_ctype.cache = None 

236 

237# Used for .ctypes attribute of ndarray 

238 

239class _missing_ctypes: 

240 def cast(self, num, obj): 

241 return num.value 

242 

243 class c_void_p: 

244 def __init__(self, ptr): 

245 self.value = ptr 

246 

247 

248class _ctypes: 

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

250 self._arr = array 

251 

252 if ctypes: 

253 self._ctypes = ctypes 

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

255 else: 

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

257 self._ctypes = _missing_ctypes() 

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

259 self._data._objects = array 

260 

261 if self._arr.ndim == 0: 

262 self._zerod = True 

263 else: 

264 self._zerod = False 

265 

266 def data_as(self, obj): 

267 """ 

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

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

270 ``self.data_as(ctypes.c_void_p)``. Perhaps you want to use the data as a 

271 pointer to a ctypes array of floating-point data: 

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

273 

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

275 """ 

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

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

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

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

280 # CPython bug https://bugs.python.org/issue12836 

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

282 ptr._arr = self._arr 

283 return ptr 

284 

285 def shape_as(self, obj): 

286 """ 

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

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

289 """ 

290 if self._zerod: 

291 return None 

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

293 

294 def strides_as(self, obj): 

295 """ 

296 Return the strides tuple as an array of some other 

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

298 """ 

299 if self._zerod: 

300 return None 

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

302 

303 @property 

304 def data(self): 

305 """ 

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

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

308 byte-order. The memory area may not even be writeable. The array 

309 flags and data-type of this array should be respected when passing this 

310 attribute to arbitrary C-code to avoid trouble that can include Python 

311 crashing. User Beware! The value of this attribute is exactly the same 

312 as ``self._array_interface_['data'][0]``. 

313 

314 Note that unlike ``data_as``, a reference will not be kept to the array: 

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

316 pointer to a deallocated array, and should be spelt 

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

318 """ 

319 return self._data.value 

320 

321 @property 

322 def shape(self): 

323 """ 

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

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

326 platform. This base-type could be `ctypes.c_int`, `ctypes.c_long`, or 

327 `ctypes.c_longlong` depending on the platform. 

328 The c_intp type is defined accordingly in `numpy.ctypeslib`. 

329 The ctypes array contains the shape of the underlying array. 

330 """ 

331 return self.shape_as(_getintp_ctype()) 

332 

333 @property 

334 def strides(self): 

335 """ 

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

337 the basetype is the same as for the shape attribute. This ctypes array 

338 contains the strides information from the underlying array. This strides 

339 information is important for showing how many bytes must be jumped to 

340 get to the next element in the array. 

341 """ 

342 return self.strides_as(_getintp_ctype()) 

343 

344 @property 

345 def _as_parameter_(self): 

346 """ 

347 Overrides the ctypes semi-magic method 

348 

349 Enables `c_func(some_array.ctypes)` 

350 """ 

351 return self.data_as(ctypes.c_void_p) 

352 

353 # kept for compatibility 

354 get_data = data.fget 

355 get_shape = shape.fget 

356 get_strides = strides.fget 

357 get_as_parameter = _as_parameter_.fget 

358 

359 

360def _newnames(datatype, order): 

361 """ 

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

363 order indicated 

364 """ 

365 oldnames = datatype.names 

366 nameslist = list(oldnames) 

367 if isinstance(order, str): 

368 order = [order] 

369 seen = set() 

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

371 for name in order: 

372 try: 

373 nameslist.remove(name) 

374 except ValueError: 

375 if name in seen: 

376 raise ValueError("duplicate field name: %s" % (name,)) 

377 else: 

378 raise ValueError("unknown field name: %s" % (name,)) 

379 seen.add(name) 

380 return tuple(list(order) + nameslist) 

381 raise ValueError("unsupported order value: %s" % (order,)) 

382 

383def _copy_fields(ary): 

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

385 

386 Parameters 

387 ---------- 

388 ary : ndarray 

389 Structured array from which to remove padding bytes 

390 

391 Returns 

392 ------- 

393 ary_copy : ndarray 

394 Copy of ary with padding bytes removed 

395 """ 

396 dt = ary.dtype 

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

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

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

400 

401def _getfield_is_safe(oldtype, newtype, offset): 

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

403 

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

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

406 

407 Parameters 

408 ---------- 

409 oldtype : data-type 

410 Data type of the original ndarray. 

411 newtype : data-type 

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

413 offset : int 

414 Offset of the field being accessed by ndarray.getfield 

415 

416 Raises 

417 ------ 

418 TypeError 

419 If the field access is invalid 

420 

421 """ 

422 if newtype.hasobject or oldtype.hasobject: 

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

424 return 

425 if oldtype.names is not None: 

426 for name in oldtype.names: 

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

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

429 return 

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

431 return 

432 

433def _view_is_safe(oldtype, newtype): 

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

435 doing:: 

436 

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

438 

439 Parameters 

440 ---------- 

441 oldtype : data-type 

442 Data type of original ndarray 

443 newtype : data-type 

444 Data type of the view 

445 

446 Raises 

447 ------ 

448 TypeError 

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

450 

451 """ 

452 

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

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

455 if oldtype == newtype: 

456 return 

457 

458 if newtype.hasobject or oldtype.hasobject: 

459 raise TypeError("Cannot change data-type for object array.") 

460 return 

461 

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

463# construct a NumPy dtype 

464 

465_pep3118_native_map = { 

466 '?': '?', 

467 'c': 'S1', 

468 'b': 'b', 

469 'B': 'B', 

470 'h': 'h', 

471 'H': 'H', 

472 'i': 'i', 

473 'I': 'I', 

474 'l': 'l', 

475 'L': 'L', 

476 'q': 'q', 

477 'Q': 'Q', 

478 'e': 'e', 

479 'f': 'f', 

480 'd': 'd', 

481 'g': 'g', 

482 'Zf': 'F', 

483 'Zd': 'D', 

484 'Zg': 'G', 

485 's': 'S', 

486 'w': 'U', 

487 'O': 'O', 

488 'x': 'V', # padding 

489} 

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

491 

492_pep3118_standard_map = { 

493 '?': '?', 

494 'c': 'S1', 

495 'b': 'b', 

496 'B': 'B', 

497 'h': 'i2', 

498 'H': 'u2', 

499 'i': 'i4', 

500 'I': 'u4', 

501 'l': 'i4', 

502 'L': 'u4', 

503 'q': 'i8', 

504 'Q': 'u8', 

505 'e': 'f2', 

506 'f': 'f', 

507 'd': 'd', 

508 'Zf': 'F', 

509 'Zd': 'D', 

510 's': 'S', 

511 'w': 'U', 

512 'O': 'O', 

513 'x': 'V', # padding 

514} 

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

516 

517_pep3118_unsupported_map = { 

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

519 '&': 'pointers', 

520 't': 'bitfields', 

521 'X': 'function pointers', 

522} 

523 

524class _Stream: 

525 def __init__(self, s): 

526 self.s = s 

527 self.byteorder = '@' 

528 

529 def advance(self, n): 

530 res = self.s[:n] 

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

532 return res 

533 

534 def consume(self, c): 

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

536 self.advance(len(c)) 

537 return True 

538 return False 

539 

540 def consume_until(self, c): 

541 if callable(c): 

542 i = 0 

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

544 i = i + 1 

545 return self.advance(i) 

546 else: 

547 i = self.s.index(c) 

548 res = self.advance(i) 

549 self.advance(len(c)) 

550 return res 

551 

552 @property 

553 def next(self): 

554 return self.s[0] 

555 

556 def __bool__(self): 

557 return bool(self.s) 

558 

559 

560def _dtype_from_pep3118(spec): 

561 stream = _Stream(spec) 

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

563 return dtype 

564 

565def __dtype_from_pep3118(stream, is_subdtype): 

566 field_spec = dict( 

567 names=[], 

568 formats=[], 

569 offsets=[], 

570 itemsize=0 

571 ) 

572 offset = 0 

573 common_alignment = 1 

574 is_padding = False 

575 

576 # Parse spec 

577 while stream: 

578 value = None 

579 

580 # End of structure, bail out to upper level 

581 if stream.consume('}'): 

582 break 

583 

584 # Sub-arrays (1) 

585 shape = None 

586 if stream.consume('('): 

587 shape = stream.consume_until(')') 

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

589 

590 # Byte order 

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

592 byteorder = stream.advance(1) 

593 if byteorder == '!': 

594 byteorder = '>' 

595 stream.byteorder = byteorder 

596 

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

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

599 type_map = _pep3118_native_map 

600 type_map_chars = _pep3118_native_typechars 

601 else: 

602 type_map = _pep3118_standard_map 

603 type_map_chars = _pep3118_standard_typechars 

604 

605 # Item sizes 

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

607 if itemsize_str: 

608 itemsize = int(itemsize_str) 

609 else: 

610 itemsize = 1 

611 

612 # Data types 

613 is_padding = False 

614 

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

616 value, align = __dtype_from_pep3118( 

617 stream, is_subdtype=True) 

618 elif stream.next in type_map_chars: 

619 if stream.next == 'Z': 

620 typechar = stream.advance(2) 

621 else: 

622 typechar = stream.advance(1) 

623 

624 is_padding = (typechar == 'x') 

625 dtypechar = type_map[typechar] 

626 if dtypechar in 'USV': 

627 dtypechar += '%d' % itemsize 

628 itemsize = 1 

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

630 stream.byteorder, stream.byteorder) 

631 value = dtype(numpy_byteorder + dtypechar) 

632 align = value.alignment 

633 elif stream.next in _pep3118_unsupported_map: 

634 desc = _pep3118_unsupported_map[stream.next] 

635 raise NotImplementedError( 

636 "Unrepresentable PEP 3118 data type {!r} ({})" 

637 .format(stream.next, desc)) 

638 else: 

639 raise ValueError("Unknown PEP 3118 data type specifier %r" % stream.s) 

640 

641 # 

642 # Native alignment may require padding 

643 # 

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

645 # that the start of the array is *already* aligned. 

646 # 

647 extra_offset = 0 

648 if stream.byteorder == '@': 

649 start_padding = (-offset) % align 

650 intra_padding = (-value.itemsize) % align 

651 

652 offset += start_padding 

653 

654 if intra_padding != 0: 

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

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

657 value = _add_trailing_padding(value, intra_padding) 

658 else: 

659 # We can postpone the injection of internal padding, 

660 # as the item appears at most once 

661 extra_offset += intra_padding 

662 

663 # Update common alignment 

664 common_alignment = _lcm(align, common_alignment) 

665 

666 # Convert itemsize to sub-array 

667 if itemsize != 1: 

668 value = dtype((value, (itemsize,))) 

669 

670 # Sub-arrays (2) 

671 if shape is not None: 

672 value = dtype((value, shape)) 

673 

674 # Field name 

675 if stream.consume(':'): 

676 name = stream.consume_until(':') 

677 else: 

678 name = None 

679 

680 if not (is_padding and name is None): 

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

682 raise RuntimeError("Duplicate field name '%s' in PEP3118 format" 

683 % name) 

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

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

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

687 

688 offset += value.itemsize 

689 offset += extra_offset 

690 

691 field_spec['itemsize'] = offset 

692 

693 # extra final padding for aligned types 

694 if stream.byteorder == '@': 

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

696 

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

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

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

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

701 and not is_subdtype): 

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

703 else: 

704 _fix_names(field_spec) 

705 ret = dtype(field_spec) 

706 

707 # Finished 

708 return ret, common_alignment 

709 

710def _fix_names(field_spec): 

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

712 names = field_spec['names'] 

713 for i, name in enumerate(names): 

714 if name is not None: 

715 continue 

716 

717 j = 0 

718 while True: 

719 name = 'f{}'.format(j) 

720 if name not in names: 

721 break 

722 j = j + 1 

723 names[i] = name 

724 

725def _add_trailing_padding(value, padding): 

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

727 if value.fields is None: 

728 field_spec = dict( 

729 names=['f0'], 

730 formats=[value], 

731 offsets=[0], 

732 itemsize=value.itemsize 

733 ) 

734 else: 

735 fields = value.fields 

736 names = value.names 

737 field_spec = dict( 

738 names=names, 

739 formats=[fields[name][0] for name in names], 

740 offsets=[fields[name][1] for name in names], 

741 itemsize=value.itemsize 

742 ) 

743 

744 field_spec['itemsize'] += padding 

745 return dtype(field_spec) 

746 

747def _prod(a): 

748 p = 1 

749 for x in a: 

750 p *= x 

751 return p 

752 

753def _gcd(a, b): 

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

755 while b: 

756 a, b = b, a % b 

757 return a 

758 

759def _lcm(a, b): 

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

761 

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

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

764 args_string = ', '.join(['{!r}'.format(arg) for arg in inputs] + 

765 ['{}={!r}'.format(k, v) 

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

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

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

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

770 '__array_ufunc__({!r}, {!r}, {}): {}' 

771 .format(ufunc, method, args_string, types_string)) 

772 

773 

774def array_function_errmsg_formatter(public_api, types): 

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

776 func_name = '{}.{}'.format(public_api.__module__, public_api.__name__) 

777 return ("no implementation found for '{}' on types that implement " 

778 '__array_function__: {}'.format(func_name, list(types))) 

779 

780 

781def _ufunc_doc_signature_formatter(ufunc): 

782 """ 

783 Builds a signature string which resembles PEP 457 

784 

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

786 """ 

787 

788 # input arguments are simple 

789 if ufunc.nin == 1: 

790 in_args = 'x' 

791 else: 

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

793 

794 # output arguments are both keyword or positional 

795 if ufunc.nout == 0: 

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

797 elif ufunc.nout == 1: 

798 out_args = ', /, out=None' 

799 else: 

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

801 positional=', '.join( 

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

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

804 ) 

805 

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

807 kwargs = ( 

808 ", casting='same_kind'" 

809 ", order='K'" 

810 ", dtype=None" 

811 ", subok=True" 

812 "[, signature" 

813 ", extobj]" 

814 ) 

815 if ufunc.signature is None: 

816 kwargs = ", where=True" + kwargs 

817 

818 # join all the parts together 

819 return '{name}({in_args}{out_args}, *{kwargs})'.format( 

820 name=ufunc.__name__, 

821 in_args=in_args, 

822 out_args=out_args, 

823 kwargs=kwargs 

824 ) 

825 

826 

827def npy_ctypes_check(cls): 

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

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

830 try: 

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

832 # for ctypes classes with multiple inheritance. 

833 if IS_PYPY: 

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

835 ctype_base = cls.__mro__[-3] 

836 else: 

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

838 ctype_base = cls.__mro__[-2] 

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

840 return '_ctypes' in ctype_base.__module__ 

841 except Exception: 

842 return False 

843 

844 

845class recursive: 

846 ''' 

847 A decorator class for recursive nested functions. 

848 Naive recursive nested functions hold a reference to themselves: 

849 

850 def outer(*args): 

851 def stringify_leaky(arg0, *arg1): 

852 if len(arg1) > 0: 

853 return stringify_leaky(*arg1) # <- HERE 

854 return str(arg0) 

855 stringify_leaky(*args) 

856 

857 This design pattern creates a reference cycle that is difficult for a 

858 garbage collector to resolve. The decorator class prevents the 

859 cycle by passing the nested function in as an argument `self`: 

860 

861 def outer(*args): 

862 @recursive 

863 def stringify(self, arg0, *arg1): 

864 if len(arg1) > 0: 

865 return self(*arg1) 

866 return str(arg0) 

867 stringify(*args) 

868 

869 ''' 

870 def __init__(self, func): 

871 self.func = func 

872 def __call__(self, *args, **kwargs): 

873 return self.func(self, *args, **kwargs) 

874