Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
_internal.py969 linesDownload Raw Back to _core
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)
969 
codekingpro/portable-devtools · Team Ai