cython

Форк
0
/
pep526_variable_annotations.py 
331 строка · 10.1 Кб
1
# cython: language_level=3
2
# mode: run
3
# tag: pure3.7, pep526, pep484, warnings
4

5
# for the benefit of the pure tests, don't require annotations
6
# to be evaluated
7
from __future__ import annotations
8

9
import cython
10

11
# Don't add FrozenSet to this list - it's necessary for one of the tests
12
# that it isn't a module global name
13
from typing import Dict, List, TypeVar, Optional, Generic, Tuple
14

15
try:
16
    import typing
17
    from typing import Set as _SET_
18
    from typing import ClassVar
19
except ImportError:
20
    pass  # this should allow Cython to interpret the directives even when the module doesn't exist
21

22

23
var = 1  # type: annotation
24
var: cython.int = 2
25
fvar: cython.float = 1.2
26
some_number: cython.int    # variable without initial value
27
some_list: List[cython.int] = []  # variable with initial value
28
another_list: list[cython.int] = []
29
t: Tuple[cython.int, ...] = (1, 2, 3)
30
t2: tuple[cython.int, ...]
31
body: Optional[List[str]]
32
descr_only : "descriptions are allowed but ignored"
33

34

35
some_number = 5
36
body = None
37

38

39
def f():
40
    """
41
    >>> f()
42
    (2, 1.5, [], (1, 2, 3))
43
    """
44
    var = 1  # type: annotation
45
    var: cython.int = 2
46
    fvar: cython.float = 1.5
47
    some_number: cython.int    # variable without initial value
48
    some_list: List[cython.int] = []  # variable with initial value
49
    t: Tuple[cython.int, ...] = (1, 2, 3)
50
    body: Optional[List[str]]
51
    descr_only: "descriptions are allowed but ignored"
52

53
    return var, fvar, some_list, t
54

55

56
class BasicStarship(object):
57
    """
58
    >>> bs = BasicStarship(5)
59
    >>> bs.damage
60
    5
61
    >>> bs.captain
62
    'Picard'
63
    >>> bs.stats
64
    {}
65
    >>> BasicStarship.stats
66
    {}
67
    """
68
    captain: str = 'Picard'               # instance variable with default
69
    damage: cython.int                    # instance variable without default
70
    stats: ClassVar[Dict[str, cython.int]] = {}  # class variable
71
    descr_only: "descriptions are allowed but ignored"
72

73
    def __init__(self, damage):
74
        self.damage = damage
75

76

77
@cython.cclass
78
class BasicStarshipExt(object):
79
    """
80
    >>> bs = BasicStarshipExt(5)
81
    >>> bs.test()
82
    (5, 'Picard', {})
83
    """
84
    captain: str = 'Picard'               # instance variable with default
85
    damage: cython.int                    # instance variable without default
86
    stats: ClassVar[Dict[str, cython.int]] = {}  # class variable
87
    descr_only: "descriptions are allowed but ignored"
88

89
    def __init__(self, damage):
90
        self.damage = damage
91

92
    def test(self):
93
        return self.damage, self.captain, self.stats
94

95

96
T = TypeVar('T')
97

98

99
# FIXME: this fails in Py3.7 now
100
#class Box(Generic[T]):
101
#    def __init__(self, content):
102
#        self.content: T = content
103
#
104
#box = Box(content=5)
105

106

107
class Cls(object):
108
    pass
109

110

111
c = Cls()
112
c.x: int = 0  # Annotates c.x with int.
113
c.y: int      # Annotates c.y with int.
114

115
d = {}
116
d['a']: int = 0  # Annotates d['a'] with int.
117
d['b']: int      # Annotates d['b'] with int.
118

119
(x): int      # Annotates x with int, (x) treated as expression by compiler.
120
(y): int = 0  # Same situation here.
121

122

123
@cython.test_assert_path_exists(
124
    "//WhileStatNode",
125
    "//WhileStatNode//DictIterationNextNode",
126
)
127
def iter_declared_dict(d):
128
    """
129
    >>> d = {1.1: 2.5, 3.3: 4.5}
130
    >>> iter_declared_dict(d)
131
    7.0
132

133
    # specialized "compiled" test in module-level __doc__
134
    """
135
    typed_dict : Dict[cython.float, cython.float] = d
136
    s = 0.0
137
    for key in typed_dict:
138
        s += d[key]
139
    return s
140

141

142
@cython.test_assert_path_exists(
143
    "//WhileStatNode",
144
    "//WhileStatNode//DictIterationNextNode",
145
)
146
def iter_declared_dict_arg(d : Dict[cython.float, cython.float]):
147
    """
148
    >>> d = {1.1: 2.5, 3.3: 4.5}
149
    >>> iter_declared_dict_arg(d)
150
    7.0
151

152
    # module level "compiled" test in __doc__ below
153
    """
154
    s = 0.0
155
    for key in d:
156
        s += d[key]
157
    return s
158

159

160
def literal_list_ptr():
161
    """
162
    >>> literal_list_ptr()
163
    4
164
    """
165
    a : cython.p_int = [1, 2, 3, 4, 5]
166
    return a[3]
167

168

169
def test_subscripted_types():
170
    """
171
    >>> test_subscripted_types()
172
    dict object
173
    dict object
174
    list object
175
    list object
176
    list object
177
    set object
178
    """
179
    a1: typing.Dict[cython.int, cython.float] = {}
180
    a2: dict[cython.int, cython.float] = {}
181
    b1: List[cython.int] = []
182
    b2: list[cython.int] = []
183
    b3: List = []  # doesn't need to be subscripted
184
    c: _SET_[object] = set()
185

186
    print(cython.typeof(a1) + (" object" if not cython.compiled else ""))
187
    print(cython.typeof(a2) + (" object" if not cython.compiled else ""))
188
    print(cython.typeof(b1) + (" object" if not cython.compiled else ""))
189
    print(cython.typeof(b2) + (" object" if not cython.compiled else ""))
190
    print(cython.typeof(b3) + (" object" if not cython.compiled else ""))
191
    print(cython.typeof(c) + (" object" if not cython.compiled else ""))
192

193
# because tuple is specifically special cased to go to ctuple where possible
194
def test_tuple(a: typing.Tuple[cython.int, cython.float], b: typing.Tuple[cython.int, ...],
195
               c: Tuple[cython.int, object]  # cannot be a ctuple
196
               ):
197
    """
198
    >>> test_tuple((1, 1.0), (1, 1.0), (1, 1.0))
199
    int
200
    int
201
    float
202
    Python object
203
    (int, float)
204
    tuple object
205
    tuple object
206
    tuple object
207
    tuple object
208
    """
209
    x: typing.Tuple[int, float] = (a[0], a[1])  # note: Python int/float, not cython.int/float
210
    y: Tuple[cython.int, ...] = (1,2.)
211
    plain_tuple: Tuple = ()
212
    z = a[0]  # should infer to C int
213
    p = x[1]  # should infer to Python float -> C double
214

215
    print(cython.typeof(z))
216
    print("int" if cython.compiled and cython.typeof(x[0]) == "Python object" else cython.typeof(x[0]))  # FIXME: infer Python int
217
    if cython.compiled:
218
        print(cython.typeof(p))
219
    else:
220
        print('float' if cython.typeof(p) == 'float' else cython.typeof(p))
221
    print(cython.typeof(x[1]) if cython.compiled or cython.typeof(p) != 'float' else "Python object")  # FIXME: infer C double
222
    print(cython.typeof(a) if cython.compiled or cython.typeof(a) != 'tuple' else "(int, float)")
223
    print(cython.typeof(x) + (" object" if not cython.compiled else ""))
224
    print(cython.typeof(y) + (" object" if not cython.compiled else ""))
225
    print(cython.typeof(c) + (" object" if not cython.compiled else ""))
226
    print(cython.typeof(plain_tuple) + (" object" if not cython.compiled else ""))
227

228

229
# because tuple is specifically special cased to go to ctuple where possible
230
def test_tuple_without_typing(a: tuple[cython.int, cython.float], b: tuple[cython.int, ...],
231
               c: tuple[cython.int, object]  # cannot be a ctuple
232
               ):
233
    """
234
    >>> test_tuple_without_typing((1, 1.0), (1, 1.0), (1, 1.0))
235
    int
236
    int
237
    float
238
    Python object
239
    (int, float)
240
    tuple object
241
    tuple object
242
    tuple object
243
    tuple object
244
    """
245
    x: tuple[int, float] = (a[0], a[1])  # note: Python int/float, not cython.int/float
246
    y: tuple[cython.int, ...] = (1,2.)
247
    plain_tuple: tuple = ()
248
    z = a[0]  # should infer to C int
249
    p = x[1]  # should infer to Python float -> C double
250

251
    print(cython.typeof(z))
252
    print("int" if cython.compiled and cython.typeof(x[0]) == "Python object" else cython.typeof(x[0]))  # FIXME: infer Python int
253
    print(cython.typeof(p) if cython.compiled or cython.typeof(p) != 'float' else "float")  # FIXME: infer C double/PyFloat from Py type
254
    print(cython.typeof(x[1]) if cython.compiled or cython.typeof(p) != 'float' else "Python object")  # FIXME: infer C double
255
    print(cython.typeof(a) if cython.compiled or cython.typeof(a) != 'tuple' else "(int, float)")
256
    print(cython.typeof(x) + (" object" if not cython.compiled else ""))
257
    print(cython.typeof(y) + (" object" if not cython.compiled else ""))
258
    print(cython.typeof(c) + (" object" if not cython.compiled else ""))
259
    print(cython.typeof(plain_tuple) + (" object" if not cython.compiled else ""))
260

261

262
def test_use_typing_attributes_as_non_annotations():
263
    """
264
    >>> test_use_typing_attributes_as_non_annotations()
265
    typing.Tuple typing.Tuple[int]
266
    typing.Optional True
267
    typing.Optional True
268
    """
269
    x1 = typing.Tuple
270
    x2 = typing.Tuple[int]
271
    y1 = typing.Optional
272
    # It's important for the test that FrozenSet isn't available in the module namespace,
273
    # since one bug would have looked it up there rather than as an attribute of typing
274
    y2 = typing.Optional[typing.FrozenSet]
275
    z1 = Optional
276
    z2 = Optional[Dict]
277
    # The result of printing "Optional[type]" is slightly version-dependent
278
    # so accept both possible forms
279
    allowed_optional_frozenset_strings = [
280
        "typing.Union[typing.FrozenSet, NoneType]",
281
        "typing.Optional[typing.FrozenSet]"
282
    ]
283
    allowed_optional_dict_strings = [
284
        "typing.Union[typing.Dict, NoneType]",
285
        "typing.Optional[typing.Dict]"
286
    ]
287
    print(x1, x2)
288
    print(y1, str(y2) in allowed_optional_frozenset_strings  or  str(y2))
289
    print(z1, str(z2) in allowed_optional_dict_strings  or  str(z2))
290

291
def test_optional_ctuple(x: typing.Optional[tuple[float]]):
292
    """
293
    Should not be a C-tuple (because these can't be optional)
294
    >>> test_optional_ctuple((1.0,))
295
    tuple object
296
    """
297
    print(cython.typeof(x) + (" object" if not cython.compiled else ""))
298

299

300
try:
301
    import numpy.typing as npt
302
    import numpy as np
303
except ImportError:
304
    # we can't actually use numpy typing right now, it was just part
305
    # of a reproducer that caused a compiler crash. We don't need it
306
    # available to use it in annotations, so don't fail if it's not there
307
    pass
308

309
def list_float_to_numpy(z: List[float]) -> List[npt.NDArray[np.float64]]:
310
    # since we're not actually requiring numpy, don't make the return type match
311
    assert cython.typeof(z) == 'list'
312
    return [z[0]]
313

314
if cython.compiled:
315
    __doc__ = """
316
    # passing non-dicts to variables declared as dict now fails
317
    >>> class D(object):
318
    ...     def __getitem__(self, x): return 2
319
    ...     def __iter__(self): return iter([1, 2, 3])
320
    >>> iter_declared_dict(D())  # doctest:+IGNORE_EXCEPTION_DETAIL
321
    Traceback (most recent call last):
322
    ...
323
    TypeError: Expected dict, got D
324
    >>> iter_declared_dict_arg(D())  # doctest:+IGNORE_EXCEPTION_DETAIL
325
    Traceback (most recent call last):
326
    ...
327
    TypeError: Expected dict, got D
328
    """
329

330
_WARNINGS = """
331
"""
332

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.