File: fastcall.pyx

package info (click to toggle)
cython 3.0.11%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 19,092 kB
  • sloc: python: 83,539; ansic: 18,831; cpp: 1,402; xml: 1,031; javascript: 511; makefile: 403; sh: 204; sed: 11
file content (140 lines) | stat: -rw-r--r-- 3,142 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# mode: run
# tag: METH_FASTCALL

cimport cython

import sys
import struct
from collections import deque

pack = struct.pack


def deque_methods(v):
    """
    >>> deque_methods(2)
    [1, 2, 3, 4]
    """
    d = deque([1, 3, 4])
    assert list(d) == [1,3,4]
    if sys.version_info >= (3, 5):
        d.insert(1, v)
    else:
        # deque has no 2-args methods in older Python versions
        d.rotate(-1)
        d.appendleft(2)
        d.rotate(1)
    assert list(d) == [1,2,3,4]
    d.rotate(len(d) // 2)
    assert list(d) == [3,4,1,2]
    d.rotate(len(d) // 2)
    assert list(d) == [1,2,3,4]

    return list(d)


def struct_methods(v):
    """
    >>> i, lf, i2, f = struct_methods(2)
    >>> struct.unpack('i', i)
    (2,)
    >>> struct.unpack('i', i2)
    (2,)
    >>> struct.unpack('lf', lf)
    (2, 4.0)
    >>> struct.unpack('f', f)
    (2.0,)
    """
    local_pack = pack
    return [
        struct.pack('i', v),
        struct.pack('lf', v, v*2),
        pack('i', v),
        local_pack('f', v),
    ]


cdef class SelfCast:
    """
    >>> f = SelfCast()
    >>> f.index_of_self([f])
    0
    >>> f.index_of_self([])  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    ValueError...
    """
    def index_of_self(self, list orbit not None):
        return orbit.index(self)


cdef extern from *:
    """
    #ifdef NDEBUG
    int DEBUG_MODE = 0;
    #else
    int DEBUG_MODE = 1;
    #endif
    """
    int PyCFunction_GET_FLAGS(op)
    int DEBUG_MODE


def has_fastcall(meth):
    """
    Given a builtin_function_or_method or cyfunction ``meth``,
    return whether it uses ``METH_FASTCALL``.
    """
    # Hardcode METH_FASTCALL constant equal to 0x80 for simplicity
    if sys.version_info >= (3, 11) and DEBUG_MODE:
        # PyCFunction_GET_FLAGS isn't safe to use on cyfunctions in
        # debug mode in Python 3.11 because it does an exact type check
        return True
    return bool(PyCFunction_GET_FLAGS(meth) & 0x80)


def assert_fastcall(meth):
    """
    Assert that ``meth`` uses ``METH_FASTCALL`` if the Python
    implementation supports it.
    """
    # getattr uses METH_FASTCALL on CPython >= 3.7
    if has_fastcall(getattr) and not has_fastcall(meth):
        raise AssertionError(f"{meth} does not use METH_FASTCALL")


@cython.binding(False)
def fastcall_function(**kw):
    """
    >>> assert_fastcall(fastcall_function)
    """
    return kw

@cython.binding(True)
def fastcall_cyfunction(**kw):
    """
    >>> assert_fastcall(fastcall_cyfunction)
    """
    return kw

cdef class Dummy:
    @cython.binding(False)
    def fastcall_method(self, x, *args, **kw):
        """
        >>> assert_fastcall(Dummy().fastcall_method)
        """
        return tuple(args) + tuple(kw)

cdef class CyDummy:
    @cython.binding(True)
    def fastcall_method(self, x, *args, **kw):
        """
        >>> assert_fastcall(CyDummy.fastcall_method)
        """
        return tuple(args) + tuple(kw)

class PyDummy:
    def fastcall_method(self, x, *args, **kw):
        """
        >>> assert_fastcall(PyDummy.fastcall_method)
        """
        return tuple(args) + tuple(kw)