Cython 3.2.3__py3-none-any.whl → 3.2.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Cython/Build/Cache.py +1 -1
- Cython/Build/Tests/TestCyCache.py +1 -0
- Cython/Build/Tests/TestInline.py +1 -1
- Cython/Compiler/Code.py +21 -3
- Cython/Compiler/CythonScope.py +3 -0
- Cython/Compiler/ExprNodes.py +4 -4
- Cython/Compiler/FlowControl.py +26 -9
- Cython/Compiler/MemoryView.py +3 -0
- Cython/Compiler/ModuleNode.py +1 -0
- Cython/Compiler/Nodes.py +13 -10
- Cython/Compiler/Optimize.py +7 -2
- Cython/Compiler/Options.py +1 -1
- Cython/Compiler/ParseTreeTransforms.py +3 -1
- Cython/Compiler/Parsing.py +11 -11
- Cython/Compiler/PyrexTypes.py +8 -2
- Cython/Compiler/Symtab.py +0 -6
- Cython/Compiler/Tests/TestBuiltin.py +11 -1
- Cython/Compiler/TreeFragment.py +2 -2
- Cython/Compiler/UtilityCode.py +4 -1
- Cython/Includes/cpython/array.pxd +3 -29
- Cython/Includes/libcpp/exception.pxd +41 -1
- Cython/Includes/libcpp/mutex.pxd +6 -6
- Cython/Runtime/refnanny.pyx +6 -6
- Cython/Shadow.py +2 -2
- Cython/Tempita/_tempita.py +0 -3
- Cython/Tests/TestShadow.py +1 -7
- Cython/Utility/Builtins.c +4 -3
- Cython/Utility/Coroutine.c +26 -29
- Cython/Utility/CpdefEnums.pyx +6 -2
- Cython/Utility/CythonFunction.c +2 -6
- Cython/Utility/FunctionArguments.c +1 -1
- Cython/Utility/MemoryView.pxd +8 -9
- Cython/Utility/MemoryView.pyx +10 -105
- Cython/Utility/MemoryView_C.c +130 -4
- Cython/Utility/ObjectHandling.c +2 -2
- Cython/Utility/Optimize.c +4 -4
- Cython/Utility/StringTools.c +8 -11
- Cython/Utility/arrayarray.h +6 -1
- Cython/Utils.py +2 -0
- cython-3.2.5.dist-info/METADATA +145 -0
- {cython-3.2.3.dist-info → cython-3.2.5.dist-info}/RECORD +44 -44
- {cython-3.2.3.dist-info → cython-3.2.5.dist-info}/WHEEL +1 -1
- cython-3.2.3.dist-info/METADATA +0 -96
- {cython-3.2.3.dist-info → cython-3.2.5.dist-info}/entry_points.txt +0 -0
- {cython-3.2.3.dist-info → cython-3.2.5.dist-info}/top_level.txt +0 -0
Cython/Build/Cache.py
CHANGED
|
@@ -152,7 +152,7 @@ class Cache:
|
|
|
152
152
|
dirname = os.path.dirname(c_file)
|
|
153
153
|
with zipfile.ZipFile(cached) as z:
|
|
154
154
|
for artifact in z.namelist():
|
|
155
|
-
z.extract(artifact,
|
|
155
|
+
z.extract(artifact, dirname)
|
|
156
156
|
else:
|
|
157
157
|
raise ValueError(f"Unsupported cache file extension: {ext}")
|
|
158
158
|
|
|
@@ -149,6 +149,7 @@ class TestCyCache(CythonTest):
|
|
|
149
149
|
|
|
150
150
|
for output in expected:
|
|
151
151
|
self.assertTrue(os.path.exists(output), output)
|
|
152
|
+
self.assertTrue(os.path.isfile(output), output)
|
|
152
153
|
|
|
153
154
|
def test_multi_file_output_cythonize(self):
|
|
154
155
|
self._test_multi_file_output(self.fresh_cythonize)
|
Cython/Build/Tests/TestInline.py
CHANGED
|
@@ -125,7 +125,7 @@ class TestCymeit(unittest.TestCase):
|
|
|
125
125
|
self.assertGreaterEqual(max_time, 100_000)
|
|
126
126
|
else:
|
|
127
127
|
self.assertGreaterEqual(max_time, 0.0001)
|
|
128
|
-
self.assertGreater(number,
|
|
128
|
+
self.assertGreater(number, 4) # arbitrary lower bound for our very quick benchmarks
|
|
129
129
|
|
|
130
130
|
return timings
|
|
131
131
|
|
Cython/Compiler/Code.py
CHANGED
|
@@ -95,7 +95,7 @@ basicsize_builtins_map = {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
# Builtins as of Python version ...
|
|
98
|
-
KNOWN_PYTHON_BUILTINS_VERSION = (3,
|
|
98
|
+
KNOWN_PYTHON_BUILTINS_VERSION = (3, 15, 0, 'beta', 1)
|
|
99
99
|
KNOWN_PYTHON_BUILTINS = frozenset([
|
|
100
100
|
'ArithmeticError',
|
|
101
101
|
'AssertionError',
|
|
@@ -125,6 +125,7 @@ KNOWN_PYTHON_BUILTINS = frozenset([
|
|
|
125
125
|
'FutureWarning',
|
|
126
126
|
'GeneratorExit',
|
|
127
127
|
'IOError',
|
|
128
|
+
'ImportCycleError',
|
|
128
129
|
'ImportError',
|
|
129
130
|
'ImportWarning',
|
|
130
131
|
'IndentationError',
|
|
@@ -176,6 +177,7 @@ KNOWN_PYTHON_BUILTINS = frozenset([
|
|
|
176
177
|
'_IncompleteInputError',
|
|
177
178
|
'__build_class__',
|
|
178
179
|
'__debug__',
|
|
180
|
+
'__lazy_import__',
|
|
179
181
|
'__import__',
|
|
180
182
|
'abs',
|
|
181
183
|
'aiter',
|
|
@@ -206,6 +208,7 @@ KNOWN_PYTHON_BUILTINS = frozenset([
|
|
|
206
208
|
'filter',
|
|
207
209
|
'float',
|
|
208
210
|
'format',
|
|
211
|
+
'frozendict',
|
|
209
212
|
'frozenset',
|
|
210
213
|
'getattr',
|
|
211
214
|
'globals',
|
|
@@ -240,6 +243,7 @@ KNOWN_PYTHON_BUILTINS = frozenset([
|
|
|
240
243
|
'repr',
|
|
241
244
|
'reversed',
|
|
242
245
|
'round',
|
|
246
|
+
'sentinel',
|
|
243
247
|
'set',
|
|
244
248
|
'setattr',
|
|
245
249
|
'slice',
|
|
@@ -257,6 +261,11 @@ KNOWN_PYTHON_BUILTINS = frozenset([
|
|
|
257
261
|
uncachable_builtins = [
|
|
258
262
|
# Global/builtin names that cannot be cached because they may or may not
|
|
259
263
|
# be available at import time, for various reasons:
|
|
264
|
+
## Python 3.15+
|
|
265
|
+
'frozendict',
|
|
266
|
+
'sentinel',
|
|
267
|
+
'ImportCycleError',
|
|
268
|
+
'__lazy_import__',
|
|
260
269
|
## Python 3.13+
|
|
261
270
|
'_IncompleteInputError',
|
|
262
271
|
'PythonFinalizationError',
|
|
@@ -267,11 +276,10 @@ uncachable_builtins = [
|
|
|
267
276
|
'aiter',
|
|
268
277
|
'anext',
|
|
269
278
|
'EncodingWarning',
|
|
270
|
-
## - Py3.7+
|
|
271
|
-
'breakpoint', # might deserve an implementation in Cython
|
|
272
279
|
## - platform specific
|
|
273
280
|
'WindowsError',
|
|
274
281
|
## - others
|
|
282
|
+
'breakpoint', # Probably best left alone.
|
|
275
283
|
'_', # e.g. used by gettext
|
|
276
284
|
]
|
|
277
285
|
|
|
@@ -2422,7 +2430,17 @@ class GlobalState:
|
|
|
2422
2430
|
writer.putln(f"PyObject **table = {array_cname};")
|
|
2423
2431
|
writer.putln(f"for (Py_ssize_t i=0; i<{constant_count}; ++i) {{")
|
|
2424
2432
|
writer.putln("#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING")
|
|
2433
|
+
# We don't want to set the refcount on shared constants (e.g. cached integers)
|
|
2434
|
+
# because setting the refcount isn't thread-safe. The chances are that most of the constants
|
|
2435
|
+
# that this applies to are already immortal though so that isn't a great loss.
|
|
2436
|
+
writer.putln("#if PY_VERSION_HEX < 0x030E0000")
|
|
2437
|
+
writer.putln("if (_Py_IsOwnedByCurrentThread(table[i]) && Py_REFCNT(table[i]) == 1)")
|
|
2438
|
+
writer.putln("#else")
|
|
2439
|
+
writer.putln("if (PyUnstable_Object_IsUniquelyReferenced(table[i]))")
|
|
2440
|
+
writer.putln("#endif")
|
|
2441
|
+
writer.putln("{")
|
|
2425
2442
|
writer.putln("Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);")
|
|
2443
|
+
writer.putln("}")
|
|
2426
2444
|
writer.putln("#else")
|
|
2427
2445
|
writer.putln("Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);")
|
|
2428
2446
|
writer.putln("#endif")
|
Cython/Compiler/CythonScope.py
CHANGED
|
@@ -6,6 +6,7 @@ from .Scanning import StringSourceDescriptor
|
|
|
6
6
|
from . import MemoryView
|
|
7
7
|
from .StringEncoding import EncodedString
|
|
8
8
|
|
|
9
|
+
NON_TYPE_NAMES = {'pointer', 'const', 'volatile', 'restrict', 'struct', 'union', 'enum'}
|
|
9
10
|
|
|
10
11
|
class CythonScope(ModuleScope):
|
|
11
12
|
is_cython_builtin = 1
|
|
@@ -42,6 +43,8 @@ class CythonScope(ModuleScope):
|
|
|
42
43
|
|
|
43
44
|
def lookup_type(self, name):
|
|
44
45
|
# This function should go away when types are all first-level objects.
|
|
46
|
+
if name in NON_TYPE_NAMES:
|
|
47
|
+
return None
|
|
45
48
|
type = parse_basic_type(name)
|
|
46
49
|
if type:
|
|
47
50
|
return type
|
Cython/Compiler/ExprNodes.py
CHANGED
|
@@ -7551,7 +7551,7 @@ class MergedDictNode(ExprNode):
|
|
|
7551
7551
|
items = ((key.constant_result, value.constant_result)
|
|
7552
7552
|
for key, value in item.key_value_pairs)
|
|
7553
7553
|
else:
|
|
7554
|
-
items = item.constant_result.
|
|
7554
|
+
items = item.constant_result.items()
|
|
7555
7555
|
|
|
7556
7556
|
for key, value in items:
|
|
7557
7557
|
if reject_duplicates and key in result:
|
|
@@ -7569,7 +7569,7 @@ class MergedDictNode(ExprNode):
|
|
|
7569
7569
|
items = [(key.compile_time_value(denv), value.compile_time_value(denv))
|
|
7570
7570
|
for key, value in item.key_value_pairs]
|
|
7571
7571
|
else:
|
|
7572
|
-
items = item.compile_time_value(denv).
|
|
7572
|
+
items = item.compile_time_value(denv).items()
|
|
7573
7573
|
|
|
7574
7574
|
try:
|
|
7575
7575
|
for key, value in items:
|
|
@@ -10491,8 +10491,8 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
|
|
|
10491
10491
|
defaults = '__Pyx_CyFunction_Defaults(struct %s, %s)' % (
|
|
10492
10492
|
self.defaults_entry.type.objstruct_cname, self.result())
|
|
10493
10493
|
for arg, entry in self.defaults:
|
|
10494
|
-
arg.
|
|
10495
|
-
|
|
10494
|
+
if arg.is_dynamic:
|
|
10495
|
+
arg.generate_assignment_code(code, cyfunc_struct_target=f'{defaults}->{entry.cname}')
|
|
10496
10496
|
|
|
10497
10497
|
if self.defaults_tuple:
|
|
10498
10498
|
code.putln('__Pyx_CyFunction_SetDefaultsTuple(%s, %s);' % (
|
Cython/Compiler/FlowControl.py
CHANGED
|
@@ -85,6 +85,18 @@ class ControlBlock:
|
|
|
85
85
|
self.children.add(block)
|
|
86
86
|
block.parents.add(self)
|
|
87
87
|
|
|
88
|
+
def print(self, level=0, seen=None):
|
|
89
|
+
if seen is None:
|
|
90
|
+
seen = set()
|
|
91
|
+
print(f"{' '*level}{self} {'*' if self in seen else ''}")
|
|
92
|
+
if self in seen:
|
|
93
|
+
return
|
|
94
|
+
for stat in self.stats:
|
|
95
|
+
print(f"{' '*(level+1)}-{stat}")
|
|
96
|
+
seen.add(self)
|
|
97
|
+
for child in self.children:
|
|
98
|
+
child.print(level+1, seen)
|
|
99
|
+
|
|
88
100
|
|
|
89
101
|
class ExitBlock(ControlBlock):
|
|
90
102
|
"""Non-empty exit point block."""
|
|
@@ -337,7 +349,8 @@ class NameAssignment:
|
|
|
337
349
|
self.is_arg = False
|
|
338
350
|
self.is_deletion = False
|
|
339
351
|
self.inferred_type = None
|
|
340
|
-
# For generator expression targets
|
|
352
|
+
# For generator expression targets in comprehensions (and possibly other things),
|
|
353
|
+
# the rhs can have a different scope than the lhs.
|
|
341
354
|
self.rhs_scope = rhs_scope
|
|
342
355
|
|
|
343
356
|
def __repr__(self):
|
|
@@ -804,7 +817,7 @@ class ControlFlowAnalysis(CythonTransform):
|
|
|
804
817
|
entry = self.env.lookup(lhs.name)
|
|
805
818
|
if entry is None: # TODO: This shouldn't happen...
|
|
806
819
|
return
|
|
807
|
-
self.flow.mark_assignment(lhs, rhs, entry, rhs_scope=rhs_scope)
|
|
820
|
+
self.flow.mark_assignment(lhs, rhs, entry, rhs_scope=(rhs_scope or self.env))
|
|
808
821
|
elif lhs.is_sequence_constructor:
|
|
809
822
|
for i, arg in enumerate(lhs.args):
|
|
810
823
|
if arg.is_starred:
|
|
@@ -925,8 +938,9 @@ class ControlFlowAnalysis(CythonTransform):
|
|
|
925
938
|
parent = self.flow.block
|
|
926
939
|
# If clauses
|
|
927
940
|
for clause in node.if_clauses:
|
|
928
|
-
|
|
941
|
+
self.flow.nextblock(parent)
|
|
929
942
|
self._visit(clause.condition)
|
|
943
|
+
parent = self.flow.block
|
|
930
944
|
self.flow.nextblock()
|
|
931
945
|
self._visit(clause.body)
|
|
932
946
|
if self.flow.block:
|
|
@@ -973,6 +987,7 @@ class ControlFlowAnalysis(CythonTransform):
|
|
|
973
987
|
self.flow.loops.append(LoopDescr(next_block, condition_block))
|
|
974
988
|
if node.condition:
|
|
975
989
|
self._visit(node.condition)
|
|
990
|
+
condition_block_end = self.flow.block
|
|
976
991
|
# Body block
|
|
977
992
|
self.flow.nextblock()
|
|
978
993
|
self._visit(node.body)
|
|
@@ -983,12 +998,12 @@ class ControlFlowAnalysis(CythonTransform):
|
|
|
983
998
|
self.flow.block.add_child(next_block)
|
|
984
999
|
# Else clause
|
|
985
1000
|
if node.else_clause:
|
|
986
|
-
self.flow.nextblock(parent=
|
|
1001
|
+
self.flow.nextblock(parent=condition_block_end)
|
|
987
1002
|
self._visit(node.else_clause)
|
|
988
1003
|
if self.flow.block:
|
|
989
1004
|
self.flow.block.add_child(next_block)
|
|
990
1005
|
else:
|
|
991
|
-
|
|
1006
|
+
condition_block_end.add_child(next_block)
|
|
992
1007
|
|
|
993
1008
|
if next_block.parents:
|
|
994
1009
|
self.flow.block = next_block
|
|
@@ -1071,6 +1086,7 @@ class ControlFlowAnalysis(CythonTransform):
|
|
|
1071
1086
|
# Condition with iterator
|
|
1072
1087
|
self.flow.loops.append(LoopDescr(next_block, condition_block))
|
|
1073
1088
|
self._visit(node.iterator)
|
|
1089
|
+
condition_block_end = self.flow.block
|
|
1074
1090
|
# Target assignment
|
|
1075
1091
|
self.flow.nextblock()
|
|
1076
1092
|
|
|
@@ -1095,12 +1111,12 @@ class ControlFlowAnalysis(CythonTransform):
|
|
|
1095
1111
|
self.flow.block.add_child(condition_block)
|
|
1096
1112
|
# Else clause
|
|
1097
1113
|
if node.else_clause:
|
|
1098
|
-
self.flow.nextblock(parent=
|
|
1114
|
+
self.flow.nextblock(parent=condition_block_end)
|
|
1099
1115
|
self._visit(node.else_clause)
|
|
1100
1116
|
if self.flow.block:
|
|
1101
1117
|
self.flow.block.add_child(next_block)
|
|
1102
1118
|
else:
|
|
1103
|
-
|
|
1119
|
+
condition_block_end.add_child(next_block)
|
|
1104
1120
|
|
|
1105
1121
|
if next_block.parents:
|
|
1106
1122
|
self.flow.block = next_block
|
|
@@ -1151,6 +1167,7 @@ class ControlFlowAnalysis(CythonTransform):
|
|
|
1151
1167
|
self._visit(node.bound2)
|
|
1152
1168
|
if node.step is not None:
|
|
1153
1169
|
self._visit(node.step)
|
|
1170
|
+
condition_block_end = self.flow.block
|
|
1154
1171
|
# Target assignment
|
|
1155
1172
|
self.flow.nextblock()
|
|
1156
1173
|
self.mark_assignment(node.target, node.bound1)
|
|
@@ -1166,12 +1183,12 @@ class ControlFlowAnalysis(CythonTransform):
|
|
|
1166
1183
|
self.flow.block.add_child(condition_block)
|
|
1167
1184
|
# Else clause
|
|
1168
1185
|
if node.else_clause:
|
|
1169
|
-
self.flow.nextblock(parent=
|
|
1186
|
+
self.flow.nextblock(parent=condition_block_end)
|
|
1170
1187
|
self._visit(node.else_clause)
|
|
1171
1188
|
if self.flow.block:
|
|
1172
1189
|
self.flow.block.add_child(next_block)
|
|
1173
1190
|
else:
|
|
1174
|
-
|
|
1191
|
+
condition_block_end.add_child(next_block)
|
|
1175
1192
|
|
|
1176
1193
|
if next_block.parents:
|
|
1177
1194
|
self.flow.block = next_block
|
Cython/Compiler/MemoryView.py
CHANGED
|
@@ -301,6 +301,7 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
|
|
|
301
301
|
util_name = "SimpleSlice"
|
|
302
302
|
else:
|
|
303
303
|
util_name = "ToughSlice"
|
|
304
|
+
code.globalstate.use_utility_code(slice_memviewslice_utility)
|
|
304
305
|
d['error_goto'] = code.error_goto(index.pos)
|
|
305
306
|
|
|
306
307
|
new_ndim += 1
|
|
@@ -847,6 +848,7 @@ refcount_utility = load_memview_c_utility("MemviewRefcount")
|
|
|
847
848
|
slice_init_utility = load_memview_c_utility("MemviewSliceInit")
|
|
848
849
|
memviewslice_declare_code = load_memview_c_utility("MemviewSliceStruct", context=template_context)
|
|
849
850
|
copy_contents_new_utility = load_memview_c_utility("MemviewSliceCopy")
|
|
851
|
+
slice_memviewslice_utility = load_memview_c_utility("SliceMemoryviewSlice")
|
|
850
852
|
|
|
851
853
|
|
|
852
854
|
@Utils.cached_function
|
|
@@ -863,6 +865,7 @@ def _get_memoryview_utility_code():
|
|
|
863
865
|
is_contig_utility,
|
|
864
866
|
overlapping_utility,
|
|
865
867
|
copy_contents_new_utility,
|
|
868
|
+
slice_memviewslice_utility,
|
|
866
869
|
],
|
|
867
870
|
)
|
|
868
871
|
|
Cython/Compiler/ModuleNode.py
CHANGED
|
@@ -1401,6 +1401,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
|
|
|
1401
1401
|
for method_entry in scope.cfunc_entries:
|
|
1402
1402
|
if not method_entry.is_inherited:
|
|
1403
1403
|
code.putln("%s;" % method_entry.type.declaration_code("(*%s)" % method_entry.cname))
|
|
1404
|
+
code.globalstate.use_entry_utility_code(method_entry)
|
|
1404
1405
|
code.putln("};")
|
|
1405
1406
|
|
|
1406
1407
|
def generate_exttype_vtabptr_declaration(self, entry, code):
|
Cython/Compiler/Nodes.py
CHANGED
|
@@ -646,11 +646,12 @@ class CArrayDeclaratorNode(CDeclaratorNode):
|
|
|
646
646
|
error(self.dimension.pos, "Array dimension cannot be const variable")
|
|
647
647
|
size = (self.dimension.constant_result if isinstance(self.dimension.constant_result, int)
|
|
648
648
|
else self.dimension.get_constant_c_result_code())
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
649
|
+
if size is not None:
|
|
650
|
+
try:
|
|
651
|
+
size = int(size)
|
|
652
|
+
except ValueError:
|
|
653
|
+
# runtime constant?
|
|
654
|
+
pass
|
|
654
655
|
|
|
655
656
|
if not base_type.is_complete():
|
|
656
657
|
error(self.pos, "Array element type '%s' is incomplete" % base_type)
|
|
@@ -1078,12 +1079,14 @@ class CArgDeclNode(Node):
|
|
|
1078
1079
|
if self.default:
|
|
1079
1080
|
self.default.annotate(code)
|
|
1080
1081
|
|
|
1081
|
-
def generate_assignment_code(self, code,
|
|
1082
|
+
def generate_assignment_code(self, code, overloaded_assignment=False,
|
|
1083
|
+
cyfunc_struct_target=None):
|
|
1082
1084
|
default = self.default
|
|
1083
|
-
if default is None or default.is_literal:
|
|
1085
|
+
if default is None or (default.is_literal and cyfunc_struct_target is None):
|
|
1084
1086
|
return
|
|
1085
|
-
if
|
|
1086
|
-
|
|
1087
|
+
# Note that even if self.is_dynamic, default may be a literal if it's been
|
|
1088
|
+
# optimized into a literal after analyse_expressions
|
|
1089
|
+
target = cyfunc_struct_target or self.calculate_default_value_code(code)
|
|
1087
1090
|
default.generate_evaluation_code(code)
|
|
1088
1091
|
default.make_owned_reference(code)
|
|
1089
1092
|
result = default.result() if overloaded_assignment else default.result_as(self.type)
|
|
@@ -4274,7 +4277,7 @@ class DefNodeWrapper(FuncDefNode):
|
|
|
4274
4277
|
f"{'' if accept_kwd_args else 'unlikely'}({Naming.kwds_cname}) ? "
|
|
4275
4278
|
f"__Pyx_NumKwargs_{self.signature.fastvar}({Naming.kwds_cname}) : 0;"
|
|
4276
4279
|
)
|
|
4277
|
-
code.putln(f"if (unlikely({Naming.kwds_len_cname}
|
|
4280
|
+
code.putln(f"if (unlikely({Naming.kwds_len_cname} < 0)) {goto_error}")
|
|
4278
4281
|
|
|
4279
4282
|
kw_unpacking_condition = f"{Naming.kwds_len_cname} > 0"
|
|
4280
4283
|
if self.num_required_kw_args > 0:
|
Cython/Compiler/Optimize.py
CHANGED
|
@@ -2882,7 +2882,11 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
|
|
|
2882
2882
|
# Map the separate type checks to check functions.
|
|
2883
2883
|
|
|
2884
2884
|
if types and (allowed_none_node or len(types) > 1):
|
|
2885
|
-
|
|
2885
|
+
# Only literals and simple (non-temp) name lookups are safe to use multiple times
|
|
2886
|
+
# without caching. Everything else — attributes, calls, walrus operators, temps,
|
|
2887
|
+
# etc. — must be evaluated exactly once and its result cached in a ResultRefNode
|
|
2888
|
+
# to avoid repeated evaluation across the short-circuit OR branches.
|
|
2889
|
+
if not (arg.is_literal or (arg.is_name and not arg.is_temp)):
|
|
2886
2890
|
arg = UtilNodes.ResultRefNode(arg)
|
|
2887
2891
|
temps.append(arg)
|
|
2888
2892
|
|
|
@@ -3531,7 +3535,8 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
|
|
|
3531
3535
|
PyUnicode_uchar_predicate_func_type = PyrexTypes.CFuncType(
|
|
3532
3536
|
PyrexTypes.c_bint_type, [
|
|
3533
3537
|
PyrexTypes.CFuncTypeArg("uchar", PyrexTypes.c_py_ucs4_type, None),
|
|
3534
|
-
]
|
|
3538
|
+
],
|
|
3539
|
+
exception_value=-1)
|
|
3535
3540
|
|
|
3536
3541
|
def _inject_unicode_predicate(self, node, function, args, is_unbound_method):
|
|
3537
3542
|
if is_unbound_method or len(args) != 1:
|
Cython/Compiler/Options.py
CHANGED
|
@@ -357,7 +357,7 @@ directive_types = {
|
|
|
357
357
|
'auto_pickle': bool,
|
|
358
358
|
'locals': dict,
|
|
359
359
|
'final' : bool, # final cdef classes and methods
|
|
360
|
-
'collection_type': one_of('sequence'),
|
|
360
|
+
'collection_type': one_of('sequence', 'mapping'),
|
|
361
361
|
'nogil' : DEFER_ANALYSIS_OF_ARGUMENTS,
|
|
362
362
|
'gil' : DEFER_ANALYSIS_OF_ARGUMENTS,
|
|
363
363
|
'critical_section' : DEFER_ANALYSIS_OF_ARGUMENTS,
|
|
@@ -1397,6 +1397,8 @@ class InterpretCompilerDirectives(CythonTransform):
|
|
|
1397
1397
|
elif isinstance(old_value, list):
|
|
1398
1398
|
old_value.extend(value)
|
|
1399
1399
|
else:
|
|
1400
|
+
if name == "collection_type" and value != optdict[name]:
|
|
1401
|
+
error(node.pos, "Multiple values of collection_type are not supported")
|
|
1400
1402
|
optdict[name] = value
|
|
1401
1403
|
else:
|
|
1402
1404
|
optdict[name] = value
|
|
@@ -2323,7 +2325,7 @@ if VALUE is not None:
|
|
|
2323
2325
|
|
|
2324
2326
|
members = ', '.join(f'self.{v}' for v in all_members_names) + (',' if len(all_members_names) == 1 else '')
|
|
2325
2327
|
# Even better, we could check PyType_IS_GC.
|
|
2326
|
-
any_notnone_members = ' or '.join([f'self.{e.name} is not None' for e in all_members if e.type.is_pyobject] or ['False'])
|
|
2328
|
+
any_notnone_members = ' or '.join([f'self.{e.name} is not None' for e in all_members if e.type.is_pyobject] or ['False'])
|
|
2327
2329
|
|
|
2328
2330
|
pickle_code = f"""
|
|
2329
2331
|
def __reduce_cython__(self):
|
Cython/Compiler/Parsing.py
CHANGED
|
@@ -550,7 +550,7 @@ def p_trailer(s: PyrexScanner, node1):
|
|
|
550
550
|
# star_expr )
|
|
551
551
|
|
|
552
552
|
@cython.cfunc
|
|
553
|
-
def p_call_parse_args(s: PyrexScanner, allow_genexp: cython.bint = True):
|
|
553
|
+
def p_call_parse_args(s: PyrexScanner, allow_genexp: cython.bint = True) -> tuple:
|
|
554
554
|
# s.sy == '('
|
|
555
555
|
s.next()
|
|
556
556
|
positional_args = []
|
|
@@ -699,7 +699,7 @@ def p_subscript_list(s: PyrexScanner) -> tuple:
|
|
|
699
699
|
#subscript: '.' '.' '.' | test | [test] ':' [test] [':' [test]]
|
|
700
700
|
|
|
701
701
|
@cython.cfunc
|
|
702
|
-
def p_subscript(s: PyrexScanner):
|
|
702
|
+
def p_subscript(s: PyrexScanner) -> list:
|
|
703
703
|
# Parse a subscript and return a list of
|
|
704
704
|
# 1, 2 or 3 ExprNodes, depending on how
|
|
705
705
|
# many slice elements were encountered.
|
|
@@ -731,7 +731,7 @@ def expect_ellipsis(s: PyrexScanner):
|
|
|
731
731
|
|
|
732
732
|
|
|
733
733
|
@cython.cfunc
|
|
734
|
-
def make_slice_nodes(pos, subscripts):
|
|
734
|
+
def make_slice_nodes(pos, subscripts) -> list:
|
|
735
735
|
# Convert a list of subscripts as returned
|
|
736
736
|
# by p_subscript_list into a list of ExprNodes,
|
|
737
737
|
# creating SliceNodes for elements with 2 or
|
|
@@ -1041,7 +1041,7 @@ def p_string_literal_shared_read(
|
|
|
1041
1041
|
return result
|
|
1042
1042
|
|
|
1043
1043
|
@cython.cfunc
|
|
1044
|
-
def _validate_kind_string(pos, systring: str):
|
|
1044
|
+
def _validate_kind_string(pos, systring: str) -> str:
|
|
1045
1045
|
kind_string = systring.rstrip('"\'').lower()
|
|
1046
1046
|
if len(kind_string) <= 1 or (len(kind_string) == 2 and kind_string in "rbrurfrtr"):
|
|
1047
1047
|
return kind_string
|
|
@@ -1137,7 +1137,7 @@ def p_string_literal(s: PyrexScanner, kind_override=None) -> tuple:
|
|
|
1137
1137
|
|
|
1138
1138
|
|
|
1139
1139
|
@cython.cfunc
|
|
1140
|
-
def p_read_ft_string_expression(s: PyrexScanner):
|
|
1140
|
+
def p_read_ft_string_expression(s: PyrexScanner) -> str:
|
|
1141
1141
|
strings = []
|
|
1142
1142
|
while True:
|
|
1143
1143
|
s.next()
|
|
@@ -1154,7 +1154,7 @@ def p_read_ft_string_expression(s: PyrexScanner):
|
|
|
1154
1154
|
@cython.cfunc
|
|
1155
1155
|
def p_ft_string_replacement_field(s: PyrexScanner,
|
|
1156
1156
|
is_raw: cython.bint, is_single_quoted: cython.bint,
|
|
1157
|
-
tf_string_kind: cython.Py_UCS4):
|
|
1157
|
+
tf_string_kind: cython.Py_UCS4) -> list:
|
|
1158
1158
|
result = []
|
|
1159
1159
|
conversion_char = format_spec = expr = None
|
|
1160
1160
|
t_string_expression = None
|
|
@@ -1264,7 +1264,7 @@ def p_ft_string_replacement_field(s: PyrexScanner,
|
|
|
1264
1264
|
def p_ft_string_middles(s: PyrexScanner,
|
|
1265
1265
|
is_raw: cython.bint, is_single_quoted: cython.bint,
|
|
1266
1266
|
is_format_string: cython.bint,
|
|
1267
|
-
tf_string_kind: cython.Py_UCS4):
|
|
1267
|
+
tf_string_kind: cython.Py_UCS4) -> list:
|
|
1268
1268
|
middles: list = []
|
|
1269
1269
|
builder = StringEncoding.UnicodeLiteralBuilder()
|
|
1270
1270
|
pos = s.position()
|
|
@@ -1302,7 +1302,7 @@ def p_ft_string_middles(s: PyrexScanner,
|
|
|
1302
1302
|
return middles
|
|
1303
1303
|
|
|
1304
1304
|
@cython.cfunc
|
|
1305
|
-
def p_ft_string_literal(s: PyrexScanner):
|
|
1305
|
+
def p_ft_string_literal(s: PyrexScanner) -> tuple:
|
|
1306
1306
|
# s.sy == BEGIN_FT_STRING
|
|
1307
1307
|
kind_string = _validate_kind_string(s.position(), s.systring)
|
|
1308
1308
|
tf_string_kind: cython.Py_UCS4 = 't' if 't' in kind_string else 'f'
|
|
@@ -1988,7 +1988,7 @@ def p_from_import_statement(s: PyrexScanner, first_statement: cython.bint = 0):
|
|
|
1988
1988
|
|
|
1989
1989
|
|
|
1990
1990
|
@cython.cfunc
|
|
1991
|
-
def p_imported_name(s: PyrexScanner):
|
|
1991
|
+
def p_imported_name(s: PyrexScanner) -> tuple:
|
|
1992
1992
|
pos = s.position()
|
|
1993
1993
|
name = p_ident(s)
|
|
1994
1994
|
as_name = p_as_name(s)
|
|
@@ -2684,7 +2684,7 @@ def p_suite_with_docstring(s: PyrexScanner, ctx, with_doc_only: cython.bint = Fa
|
|
|
2684
2684
|
|
|
2685
2685
|
|
|
2686
2686
|
@cython.cfunc
|
|
2687
|
-
def p_positional_and_keyword_args(s: PyrexScanner, end_sy_set, templates = None):
|
|
2687
|
+
def p_positional_and_keyword_args(s: PyrexScanner, end_sy_set, templates = None) -> tuple:
|
|
2688
2688
|
"""
|
|
2689
2689
|
Parses positional and keyword arguments. end_sy_set
|
|
2690
2690
|
should contain any s.sy that terminate the argument list.
|
|
@@ -4713,7 +4713,7 @@ def p_class_pattern(s: PyrexScanner):
|
|
|
4713
4713
|
|
|
4714
4714
|
|
|
4715
4715
|
@cython.cfunc
|
|
4716
|
-
def p_keyword_pattern(s: PyrexScanner):
|
|
4716
|
+
def p_keyword_pattern(s: PyrexScanner) -> tuple:
|
|
4717
4717
|
if s.sy != "IDENT":
|
|
4718
4718
|
s.error("Expected identifier")
|
|
4719
4719
|
arg = p_name(s, s.systring)
|
Cython/Compiler/PyrexTypes.py
CHANGED
|
@@ -4689,6 +4689,8 @@ class CTupleType(CType):
|
|
|
4689
4689
|
|
|
4690
4690
|
_convert_to_py_code = None
|
|
4691
4691
|
_convert_from_py_code = None
|
|
4692
|
+
to_py_function = None
|
|
4693
|
+
from_py_function = None
|
|
4692
4694
|
|
|
4693
4695
|
def __init__(self, cname, components):
|
|
4694
4696
|
from .Builtin import tuple_type
|
|
@@ -4696,8 +4698,6 @@ class CTupleType(CType):
|
|
|
4696
4698
|
self.components = components
|
|
4697
4699
|
self.equivalent_type = tuple_type
|
|
4698
4700
|
self.size = len(components)
|
|
4699
|
-
self.to_py_function = f"{Naming.convert_func_prefix}_to_py_{self.cname}"
|
|
4700
|
-
self.from_py_function = f"{Naming.convert_func_prefix}_from_py_{self.cname}"
|
|
4701
4701
|
|
|
4702
4702
|
def __str__(self):
|
|
4703
4703
|
return "(%s)" % ", ".join(str(c) for c in self.components)
|
|
@@ -4732,6 +4732,7 @@ class CTupleType(CType):
|
|
|
4732
4732
|
return False
|
|
4733
4733
|
|
|
4734
4734
|
if self._convert_to_py_code is None:
|
|
4735
|
+
self.to_py_function = f"{Naming.convert_func_prefix}_to_py_{self.cname}"
|
|
4735
4736
|
context = dict(
|
|
4736
4737
|
struct_type_decl=self.empty_declaration_code(),
|
|
4737
4738
|
components=self.components,
|
|
@@ -4755,6 +4756,7 @@ class CTupleType(CType):
|
|
|
4755
4756
|
return False
|
|
4756
4757
|
|
|
4757
4758
|
if self._convert_from_py_code is None:
|
|
4759
|
+
self.from_py_function = f"{Naming.convert_func_prefix}_from_py_{self.cname}"
|
|
4758
4760
|
context = dict(
|
|
4759
4761
|
struct_type_decl=self.empty_declaration_code(),
|
|
4760
4762
|
components=self.components,
|
|
@@ -5545,6 +5547,10 @@ def independent_spanning_type(type1, type2):
|
|
|
5545
5547
|
return resolved_type1
|
|
5546
5548
|
# e.g. PyInt + double => object
|
|
5547
5549
|
return py_object_type
|
|
5550
|
+
elif resolved_type1.is_builtin_type and resolved_type2.is_builtin_type:
|
|
5551
|
+
# Either numeric or incompatible. Do not try to find a widest Python type
|
|
5552
|
+
# (e.g. int+float => float) as it would change one of the result types.
|
|
5553
|
+
return py_object_type
|
|
5548
5554
|
|
|
5549
5555
|
span_type = _spanning_type(type1, type2)
|
|
5550
5556
|
if span_type is None:
|
Cython/Compiler/Symtab.py
CHANGED
|
@@ -1901,12 +1901,6 @@ class ModuleScope(Scope):
|
|
|
1901
1901
|
if self.directives.get('final'):
|
|
1902
1902
|
entry.type.is_final_type = True
|
|
1903
1903
|
collection_type = self.directives.get('collection_type')
|
|
1904
|
-
if collection_type:
|
|
1905
|
-
from .UtilityCode import NonManglingModuleScope
|
|
1906
|
-
if not isinstance(self, NonManglingModuleScope):
|
|
1907
|
-
# TODO - DW would like to make it public, but I'm making it internal-only
|
|
1908
|
-
# for now to avoid adding new features without consensus
|
|
1909
|
-
error(pos, "'collection_type' is not a public cython directive")
|
|
1910
1904
|
if collection_type == 'sequence':
|
|
1911
1905
|
entry.type.has_sequence_flag = True
|
|
1912
1906
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import builtins
|
|
2
|
+
import json
|
|
3
|
+
import subprocess
|
|
2
4
|
import sys
|
|
3
5
|
import unittest
|
|
4
6
|
|
|
@@ -41,9 +43,17 @@ class TestBuiltinCompatibility(unittest.TestCase):
|
|
|
41
43
|
expected_builtins = set(KNOWN_PYTHON_BUILTINS)
|
|
42
44
|
if sys.platform != 'win32':
|
|
43
45
|
expected_builtins.discard("WindowsError")
|
|
46
|
+
|
|
47
|
+
# Read builtins from fresh Python process to prevent modifications by test dependencies.
|
|
48
|
+
output = subprocess.run(
|
|
49
|
+
[sys.executable, '-c', 'import builtins, json, sys; sys.stdout.write(json.dumps(dir(builtins)))'],
|
|
50
|
+
capture_output=True,
|
|
51
|
+
encoding='utf8',
|
|
52
|
+
)
|
|
44
53
|
runtime_builtins = frozenset(
|
|
45
|
-
name for name in
|
|
54
|
+
name for name in json.loads(output.stdout)
|
|
46
55
|
if name not in ('__doc__', '__loader__', '__name__', '__package__', '__spec__'))
|
|
56
|
+
|
|
47
57
|
if sys.version_info < KNOWN_PYTHON_BUILTINS_VERSION:
|
|
48
58
|
missing_builtins = expected_builtins - runtime_builtins
|
|
49
59
|
if missing_builtins:
|
Cython/Compiler/TreeFragment.py
CHANGED
|
@@ -22,12 +22,12 @@ from . import UtilNodes
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class StringParseContext(Main.Context):
|
|
25
|
-
def __init__(self, name, include_directories=None, compiler_directives=None, cpp=False):
|
|
25
|
+
def __init__(self, name, include_directories=None, compiler_directives=None, cpp=False, options=None):
|
|
26
26
|
if include_directories is None:
|
|
27
27
|
include_directories = []
|
|
28
28
|
if compiler_directives is None:
|
|
29
29
|
compiler_directives = {}
|
|
30
|
-
Main.Context.__init__(self, include_directories, compiler_directives, cpp=cpp, language_level='3')
|
|
30
|
+
Main.Context.__init__(self, include_directories, compiler_directives, cpp=cpp, language_level='3', options=options)
|
|
31
31
|
self.module_name = name
|
|
32
32
|
|
|
33
33
|
def find_module(self, module_name, from_module=None, pos=None, need_pxd=1, absolute_fallback=True, relative_import=False):
|
Cython/Compiler/UtilityCode.py
CHANGED
|
@@ -125,7 +125,8 @@ class CythonUtilityCode(Code.UtilityCodeBase):
|
|
|
125
125
|
from . import Pipeline, ParseTreeTransforms
|
|
126
126
|
context = CythonUtilityCodeContext(
|
|
127
127
|
self.name, compiler_directives=self.compiler_directives,
|
|
128
|
-
cpp=cython_scope.is_cpp() if cython_scope else False
|
|
128
|
+
cpp=cython_scope.is_cpp() if cython_scope else False,
|
|
129
|
+
options=cython_scope.context.options if cython_scope else None)
|
|
129
130
|
context.prefix = self.prefix
|
|
130
131
|
context.cython_scope = cython_scope
|
|
131
132
|
#context = StringParseContext(self.name)
|
|
@@ -327,6 +328,8 @@ class CythonSharedUtilityCode(Code.AbstractUtilityCode):
|
|
|
327
328
|
dep.declare_in_scope(scope, cython_scope=cython_scope)
|
|
328
329
|
for e in self._shared_library_scope.c_class_entries:
|
|
329
330
|
dest_scope.add_imported_entry(e.name, e, e.pos)
|
|
331
|
+
for e in self._shared_library_scope.var_entries:
|
|
332
|
+
dest_scope.add_imported_entry(e.name, e, e.pos)
|
|
330
333
|
return dest_scope
|
|
331
334
|
|
|
332
335
|
def get_shared_library_scope(self, cython_scope):
|