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.
Files changed (45) hide show
  1. Cython/Build/Cache.py +1 -1
  2. Cython/Build/Tests/TestCyCache.py +1 -0
  3. Cython/Build/Tests/TestInline.py +1 -1
  4. Cython/Compiler/Code.py +21 -3
  5. Cython/Compiler/CythonScope.py +3 -0
  6. Cython/Compiler/ExprNodes.py +4 -4
  7. Cython/Compiler/FlowControl.py +26 -9
  8. Cython/Compiler/MemoryView.py +3 -0
  9. Cython/Compiler/ModuleNode.py +1 -0
  10. Cython/Compiler/Nodes.py +13 -10
  11. Cython/Compiler/Optimize.py +7 -2
  12. Cython/Compiler/Options.py +1 -1
  13. Cython/Compiler/ParseTreeTransforms.py +3 -1
  14. Cython/Compiler/Parsing.py +11 -11
  15. Cython/Compiler/PyrexTypes.py +8 -2
  16. Cython/Compiler/Symtab.py +0 -6
  17. Cython/Compiler/Tests/TestBuiltin.py +11 -1
  18. Cython/Compiler/TreeFragment.py +2 -2
  19. Cython/Compiler/UtilityCode.py +4 -1
  20. Cython/Includes/cpython/array.pxd +3 -29
  21. Cython/Includes/libcpp/exception.pxd +41 -1
  22. Cython/Includes/libcpp/mutex.pxd +6 -6
  23. Cython/Runtime/refnanny.pyx +6 -6
  24. Cython/Shadow.py +2 -2
  25. Cython/Tempita/_tempita.py +0 -3
  26. Cython/Tests/TestShadow.py +1 -7
  27. Cython/Utility/Builtins.c +4 -3
  28. Cython/Utility/Coroutine.c +26 -29
  29. Cython/Utility/CpdefEnums.pyx +6 -2
  30. Cython/Utility/CythonFunction.c +2 -6
  31. Cython/Utility/FunctionArguments.c +1 -1
  32. Cython/Utility/MemoryView.pxd +8 -9
  33. Cython/Utility/MemoryView.pyx +10 -105
  34. Cython/Utility/MemoryView_C.c +130 -4
  35. Cython/Utility/ObjectHandling.c +2 -2
  36. Cython/Utility/Optimize.c +4 -4
  37. Cython/Utility/StringTools.c +8 -11
  38. Cython/Utility/arrayarray.h +6 -1
  39. Cython/Utils.py +2 -0
  40. cython-3.2.5.dist-info/METADATA +145 -0
  41. {cython-3.2.3.dist-info → cython-3.2.5.dist-info}/RECORD +44 -44
  42. {cython-3.2.3.dist-info → cython-3.2.5.dist-info}/WHEEL +1 -1
  43. cython-3.2.3.dist-info/METADATA +0 -96
  44. {cython-3.2.3.dist-info → cython-3.2.5.dist-info}/entry_points.txt +0 -0
  45. {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, join_path(dirname, 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)
@@ -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, 10) # arbitrary lower bound for our very quick benchmarks
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, 14, 0, 'beta', 1)
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")
@@ -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
@@ -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.iteritems()
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).iteritems()
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.generate_assignment_code(code, target='%s->%s' % (
10495
- defaults, entry.cname))
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);' % (
@@ -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, the rhs can have a different scope than the lhs.
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
- parent = self.flow.nextblock(parent)
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=condition_block)
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
- condition_block.add_child(next_block)
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=condition_block)
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
- condition_block.add_child(next_block)
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=condition_block)
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
- condition_block.add_child(next_block)
1191
+ condition_block_end.add_child(next_block)
1175
1192
 
1176
1193
  if next_block.parents:
1177
1194
  self.flow.block = next_block
@@ -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
 
@@ -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
- try:
650
- size = int(size)
651
- except ValueError:
652
- # runtime constant?
653
- pass
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, target=None, overloaded_assignment=False):
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 target is None:
1086
- target = self.calculate_default_value_code(code)
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}) < 0) {goto_error}")
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:
@@ -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
- if arg.is_attribute or not arg.is_simple():
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:
@@ -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):
@@ -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)
@@ -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 dir(builtins)
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:
@@ -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):
@@ -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):