Cython 3.2.4__py3-none-any.whl → 3.2.6__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 (44) hide show
  1. Cython/Build/Cache.py +1 -1
  2. Cython/Build/Dependencies.py +1 -1
  3. Cython/Build/SharedModule.py +8 -17
  4. Cython/Build/Tests/TestCyCache.py +1 -0
  5. Cython/Build/Tests/TestInline.py +1 -1
  6. Cython/Compiler/Code.py +20 -5
  7. Cython/Compiler/CythonScope.py +3 -0
  8. Cython/Compiler/ExprNodes.py +2 -2
  9. Cython/Compiler/FlowControl.py +23 -7
  10. Cython/Compiler/MemoryView.py +3 -0
  11. Cython/Compiler/ModuleNode.py +1 -0
  12. Cython/Compiler/Nodes.py +7 -6
  13. Cython/Compiler/Optimize.py +7 -2
  14. Cython/Compiler/PyrexTypes.py +8 -2
  15. Cython/Compiler/Scanning.py +9 -0
  16. Cython/Compiler/Tests/TestBuiltin.py +11 -1
  17. Cython/Compiler/TreeFragment.py +2 -2
  18. Cython/Compiler/UtilityCode.py +4 -1
  19. Cython/Includes/cpython/array.pxd +3 -29
  20. Cython/Includes/libcpp/mutex.pxd +6 -6
  21. Cython/Includes/libcpp/vector.pxd +4 -4
  22. Cython/Runtime/refnanny.pyx +6 -6
  23. Cython/Shadow.py +1 -1
  24. Cython/Tempita/_tempita.py +0 -3
  25. Cython/Utility/Builtins.c +1 -0
  26. Cython/Utility/Coroutine.c +26 -29
  27. Cython/Utility/CpdefEnums.pyx +6 -2
  28. Cython/Utility/CythonFunction.c +56 -6
  29. Cython/Utility/FunctionArguments.c +1 -1
  30. Cython/Utility/MemoryView.pxd +8 -9
  31. Cython/Utility/MemoryView.pyx +10 -105
  32. Cython/Utility/MemoryView_C.c +130 -4
  33. Cython/Utility/ObjectHandling.c +2 -2
  34. Cython/Utility/Optimize.c +4 -4
  35. Cython/Utility/StringTools.c +6 -9
  36. Cython/Utility/TString.c +1 -1
  37. Cython/Utility/arrayarray.h +6 -1
  38. Cython/Utils.py +2 -0
  39. cython-3.2.6.dist-info/METADATA +93 -0
  40. {cython-3.2.4.dist-info → cython-3.2.6.dist-info}/RECORD +43 -43
  41. {cython-3.2.4.dist-info → cython-3.2.6.dist-info}/WHEEL +1 -1
  42. cython-3.2.4.dist-info/METADATA +0 -158
  43. {cython-3.2.4.dist-info → cython-3.2.6.dist-info}/entry_points.txt +0 -0
  44. {cython-3.2.4.dist-info → cython-3.2.6.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
 
@@ -52,7 +52,7 @@ def extended_iglob(pattern):
52
52
  # because '/' is generally common for relative paths.
53
53
  if '**/' in pattern or os.sep == '\\' and '**\\' in pattern:
54
54
  seen = set()
55
- first, rest = re.split(r'\*\*[%s]' % ('/\\\\' if os.sep == '\\' else '/'), pattern, 1)
55
+ first, rest = re.split(r'\*\*[%s]' % ('/\\\\' if os.sep == '\\' else '/'), pattern, maxsplit=1)
56
56
  if first:
57
57
  first = iglob(first + os.sep)
58
58
  else:
@@ -1,13 +1,10 @@
1
1
  import os
2
- import re
3
- import shutil
4
- import tempfile
5
2
 
6
3
  from Cython.Compiler import (
7
4
  MemoryView, Code, Options, Pipeline, Errors, Main, Symtab
8
5
  )
9
6
  from Cython.Compiler.StringEncoding import EncodedString
10
- from Cython.Compiler.Scanning import FileSourceDescriptor
7
+ from Cython.Compiler.Scanning import SharedUtilitySourceDescriptor
11
8
 
12
9
 
13
10
  def create_shared_library_pipeline(context, scope, options, result):
@@ -72,23 +69,17 @@ def generate_shared_module(options):
72
69
  Errors.open_listing_file(None)
73
70
 
74
71
  dest_c_file = options.shared_c_file_path
72
+ pyx_file = os.path.splitext(dest_c_file)[0] + '.pyx'
75
73
  module_name = os.path.splitext(os.path.basename(dest_c_file))[0]
76
74
 
77
75
  context = Main.Context.from_options(options)
78
76
  scope = Symtab.ModuleScope('MemoryView', parent_module = None, context = context, is_package=False)
79
77
 
80
- with tempfile.TemporaryDirectory() as tmpdirname:
81
- pyx_file = os.path.join(tmpdirname, f'{module_name}.pyx')
82
- c_file = os.path.join(tmpdirname, f'{module_name}.c')
83
- with open(pyx_file, 'w'):
84
- pass
85
- source_desc = FileSourceDescriptor(pyx_file)
86
- comp_src = Main.CompilationSource(source_desc, EncodedString(module_name), os.getcwd())
87
- result = Main.create_default_resultobj(comp_src, options)
88
-
89
- pipeline = create_shared_library_pipeline(context, scope, options, result)
90
- err, enddata = Pipeline.run_pipeline(pipeline, comp_src)
91
- if err is None:
92
- shutil.copy(c_file, dest_c_file)
78
+ source_desc = SharedUtilitySourceDescriptor(pyx_file)
79
+ comp_src = Main.CompilationSource(source_desc, EncodedString(module_name), os.getcwd())
80
+ result = Main.create_default_resultobj(comp_src, options)
81
+
82
+ pipeline = create_shared_library_pipeline(context, scope, options, result)
83
+ err, enddata = Pipeline.run_pipeline(pipeline, comp_src)
93
84
 
94
85
  return err, enddata
@@ -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
 
@@ -2421,19 +2429,26 @@ class GlobalState:
2421
2429
  writer.putln("{")
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
- writer.putln("#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING")
2432
+ writer.putln("#if PY_VERSION_HEX >= 0x030F0000")
2433
+ writer.putln("PyUnstable_SetImmortal(table[i]);")
2434
+ writer.putln("#elif CYTHON_COMPILING_IN_CPYTHON_FREETHREADING")
2425
2435
  # We don't want to set the refcount on shared constants (e.g. cached integers)
2426
2436
  # because setting the refcount isn't thread-safe. The chances are that most of the constants
2427
2437
  # that this applies to are already immortal though so that isn't a great loss.
2438
+ # Overflow, e.g. on 32 bit systems.
2439
+ writer.putln("if ((PY_SSIZE_T_MAX <= _Py_IMMORTAL_REFCNT_LOCAL)) break;")
2428
2440
  writer.putln("#if PY_VERSION_HEX < 0x030E0000")
2429
2441
  writer.putln("if (_Py_IsOwnedByCurrentThread(table[i]) && Py_REFCNT(table[i]) == 1)")
2430
2442
  writer.putln("#else")
2431
2443
  writer.putln("if (PyUnstable_Object_IsUniquelyReferenced(table[i]))")
2432
2444
  writer.putln("#endif")
2433
2445
  writer.putln("{")
2434
- writer.putln("Py_SET_REFCNT(table[i], _Py_IMMORTAL_REFCNT_LOCAL);")
2446
+ # Go one higher than we think we need to because of a bug in SET_REFCNT check in CPython
2447
+ writer.putln("Py_SET_REFCNT(table[i], ((Py_ssize_t)_Py_IMMORTAL_REFCNT_LOCAL + 1));")
2435
2448
  writer.putln("}")
2436
2449
  writer.putln("#else")
2450
+ # Overflow, e.g. on 32 bit systems.
2451
+ writer.putln("if ((PY_SSIZE_T_MAX < _Py_IMMORTAL_INITIAL_REFCNT)) break;")
2437
2452
  writer.putln("Py_SET_REFCNT(table[i], _Py_IMMORTAL_INITIAL_REFCNT);")
2438
2453
  writer.putln("#endif")
2439
2454
  writer.putln("}") # for()
@@ -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:
@@ -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."""
@@ -926,8 +938,9 @@ class ControlFlowAnalysis(CythonTransform):
926
938
  parent = self.flow.block
927
939
  # If clauses
928
940
  for clause in node.if_clauses:
929
- parent = self.flow.nextblock(parent)
941
+ self.flow.nextblock(parent)
930
942
  self._visit(clause.condition)
943
+ parent = self.flow.block
931
944
  self.flow.nextblock()
932
945
  self._visit(clause.body)
933
946
  if self.flow.block:
@@ -974,6 +987,7 @@ class ControlFlowAnalysis(CythonTransform):
974
987
  self.flow.loops.append(LoopDescr(next_block, condition_block))
975
988
  if node.condition:
976
989
  self._visit(node.condition)
990
+ condition_block_end = self.flow.block
977
991
  # Body block
978
992
  self.flow.nextblock()
979
993
  self._visit(node.body)
@@ -984,12 +998,12 @@ class ControlFlowAnalysis(CythonTransform):
984
998
  self.flow.block.add_child(next_block)
985
999
  # Else clause
986
1000
  if node.else_clause:
987
- self.flow.nextblock(parent=condition_block)
1001
+ self.flow.nextblock(parent=condition_block_end)
988
1002
  self._visit(node.else_clause)
989
1003
  if self.flow.block:
990
1004
  self.flow.block.add_child(next_block)
991
1005
  else:
992
- condition_block.add_child(next_block)
1006
+ condition_block_end.add_child(next_block)
993
1007
 
994
1008
  if next_block.parents:
995
1009
  self.flow.block = next_block
@@ -1072,6 +1086,7 @@ class ControlFlowAnalysis(CythonTransform):
1072
1086
  # Condition with iterator
1073
1087
  self.flow.loops.append(LoopDescr(next_block, condition_block))
1074
1088
  self._visit(node.iterator)
1089
+ condition_block_end = self.flow.block
1075
1090
  # Target assignment
1076
1091
  self.flow.nextblock()
1077
1092
 
@@ -1096,12 +1111,12 @@ class ControlFlowAnalysis(CythonTransform):
1096
1111
  self.flow.block.add_child(condition_block)
1097
1112
  # Else clause
1098
1113
  if node.else_clause:
1099
- self.flow.nextblock(parent=condition_block)
1114
+ self.flow.nextblock(parent=condition_block_end)
1100
1115
  self._visit(node.else_clause)
1101
1116
  if self.flow.block:
1102
1117
  self.flow.block.add_child(next_block)
1103
1118
  else:
1104
- condition_block.add_child(next_block)
1119
+ condition_block_end.add_child(next_block)
1105
1120
 
1106
1121
  if next_block.parents:
1107
1122
  self.flow.block = next_block
@@ -1152,6 +1167,7 @@ class ControlFlowAnalysis(CythonTransform):
1152
1167
  self._visit(node.bound2)
1153
1168
  if node.step is not None:
1154
1169
  self._visit(node.step)
1170
+ condition_block_end = self.flow.block
1155
1171
  # Target assignment
1156
1172
  self.flow.nextblock()
1157
1173
  self.mark_assignment(node.target, node.bound1)
@@ -1167,12 +1183,12 @@ class ControlFlowAnalysis(CythonTransform):
1167
1183
  self.flow.block.add_child(condition_block)
1168
1184
  # Else clause
1169
1185
  if node.else_clause:
1170
- self.flow.nextblock(parent=condition_block)
1186
+ self.flow.nextblock(parent=condition_block_end)
1171
1187
  self._visit(node.else_clause)
1172
1188
  if self.flow.block:
1173
1189
  self.flow.block.add_child(next_block)
1174
1190
  else:
1175
- condition_block.add_child(next_block)
1191
+ condition_block_end.add_child(next_block)
1176
1192
 
1177
1193
  if next_block.parents:
1178
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)
@@ -4276,7 +4277,7 @@ class DefNodeWrapper(FuncDefNode):
4276
4277
  f"{'' if accept_kwd_args else 'unlikely'}({Naming.kwds_cname}) ? "
4277
4278
  f"__Pyx_NumKwargs_{self.signature.fastvar}({Naming.kwds_cname}) : 0;"
4278
4279
  )
4279
- 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}")
4280
4281
 
4281
4282
  kw_unpacking_condition = f"{Naming.kwds_len_cname} > 0"
4282
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:
@@ -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:
@@ -281,6 +281,15 @@ class StringSourceDescriptor(SourceDescriptor):
281
281
  return "<StringSourceDescriptor:%s>" % self.name
282
282
 
283
283
 
284
+ class SharedUtilitySourceDescriptor(FileSourceDescriptor):
285
+ """
286
+ A specialized source descriptor for shared utility code only. Not part of public API.
287
+ """
288
+
289
+ def get_file_object(self, encoding=None, error_handling=None):
290
+ from io import StringIO
291
+ return StringIO('')
292
+
284
293
  #------------------------------------------------------------------
285
294
 
286
295
  class PyrexScanner(Scanner):
@@ -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):
@@ -70,7 +70,9 @@ cdef extern from *: # Hard-coded utility code hack.
70
70
  ctypedef object GETF(array a, Py_ssize_t ix)
71
71
  ctypedef object SETF(array a, Py_ssize_t ix, object o)
72
72
  ctypedef struct arraydescr: # [object arraydescr]:
73
- char typecode
73
+ char typecode "typecode_char" # backwards compatibility only
74
+ char typecode_char # Python <= 3.14
75
+ char typecode_array[3] # Python 3.15+
74
76
  int itemsize
75
77
  GETF getitem # PyObject * (*getitem)(struct arrayobject *, Py_ssize_t);
76
78
  SETF setitem # int (*setitem)(struct arrayobject *, Py_ssize_t, PyObject *);
@@ -104,34 +106,6 @@ cdef extern from *: # Hard-coded utility code hack.
104
106
  cdef inline __data_union data(self) noexcept nogil:
105
107
  return __Pyx_PyArray_Data(self)
106
108
 
107
- def __getbuffer__(self, Py_buffer* info, int flags):
108
- # This implementation of getbuffer is geared towards Cython
109
- # requirements, and does not yet fulfill the PEP.
110
- # In particular strided access is always provided regardless
111
- # of flags
112
- item_count = Py_SIZE(self)
113
-
114
- info.suboffsets = NULL
115
- info.buf = self.data.as_chars
116
- info.readonly = 0
117
- info.ndim = 1
118
- info.itemsize = self.ob_descr.itemsize # e.g. sizeof(float)
119
- info.len = info.itemsize * item_count
120
-
121
- info.shape = <Py_ssize_t*> PyObject_Malloc(sizeof(Py_ssize_t) + 2)
122
- if not info.shape:
123
- raise MemoryError()
124
- info.shape[0] = item_count # constant regardless of resizing
125
- info.strides = &info.itemsize
126
-
127
- info.format = <char*> (info.shape + 1)
128
- info.format[0] = self.ob_descr.typecode
129
- info.format[1] = 0
130
- info.obj = self
131
-
132
- def __releasebuffer__(self, Py_buffer* info):
133
- PyObject_Free(info.shape)
134
-
135
109
  array newarrayobject(PyTypeObject* type, Py_ssize_t size, arraydescr *descr)
136
110
 
137
111
  __data_union __Pyx_PyArray_Data(array self) noexcept nogil
@@ -158,7 +158,7 @@ cdef extern from *:
158
158
 
159
159
  namespace {
160
160
 
161
- static PyGILState_STATE __pyx_libcpp_mutex_limited_api_ensure_gil() {
161
+ CYTHON_UNUSED PyGILState_STATE __pyx_libcpp_mutex_limited_api_ensure_gil() {
162
162
  #if CYTHON_COMPILING_IN_LIMITED_API
163
163
  if ((__PYX_LIMITED_VERSION_HEX < 0x030d0000) && __Pyx_get_runtime_version() < 0x030d0000) {
164
164
  return PyGILState_Ensure();
@@ -168,7 +168,7 @@ cdef extern from *:
168
168
  }
169
169
 
170
170
  #if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030d0000
171
- static void __pyx_libcpp_mutex_limited_api_release_gil(PyGILState_STATE gil_state) {
171
+ CYTHON_UNUSED void __pyx_libcpp_mutex_limited_api_release_gil(PyGILState_STATE gil_state) {
172
172
  if (__Pyx_get_runtime_version() < 0x030d0000)
173
173
  PyGILState_Release(gil_state);
174
174
  }
@@ -176,7 +176,7 @@ cdef extern from *:
176
176
  #define __pyx_libcpp_mutex_limited_api_release_gil(ignore) (void)ignore
177
177
  #endif
178
178
 
179
- static int __pyx_libcpp_mutex_has_gil() {
179
+ CYTHON_UNUSED int __pyx_libcpp_mutex_has_gil() {
180
180
  #if CYTHON_COMPILING_IN_LIMITED_API
181
181
  if ((__PYX_LIMITED_VERSION_HEX >= 0x030d0000) || __Pyx_get_runtime_version() >= 0x030d0000) {
182
182
  // In 3.13+ we can temporarily give up the GIL to find out what the thread state was
@@ -232,7 +232,7 @@ cdef extern from *:
232
232
  }
233
233
 
234
234
  template <typename Callable, typename ... Args>
235
- void __pyx_cpp_py_safe_call_once(std::once_flag& flag, Callable& callable, Args&&... args) {
235
+ void __pyx_cpp_py_safe_call_once(std::once_flag& flag, Callable&& callable, Args&&... args) {
236
236
  class PyException : public std::exception {
237
237
  public:
238
238
  using std::exception::exception;
@@ -246,7 +246,7 @@ cdef extern from *:
246
246
 
247
247
  try {
248
248
  std::call_once(flag,
249
- [&](Args& ...args) {
249
+ [&](Args&& ...args) {
250
250
  // Make sure we have the GIL
251
251
  PyGILState_STATE gil_state;
252
252
  int had_gil_on_call = __Pyx_UnknownThreadStateDefinitelyHadGil(thread_state);
@@ -296,7 +296,7 @@ cdef extern from *:
296
296
  std::lock(arg0, arg1, args...);
297
297
  }
298
298
 
299
- inline void __pyx_libcpp_mutex_unlock() {} // no-op
299
+ CYTHON_UNUSED inline void __pyx_libcpp_mutex_unlock() {} // no-op
300
300
 
301
301
  template <typename Lockable0T, typename ... Lockables>
302
302
  void __pyx_libcpp_mutex_unlock(Lockable0T& arg0, Lockables&... locks) {
@@ -79,8 +79,8 @@ cdef extern from "<vector>" namespace "std" nogil:
79
79
  reverse_iterator operator--(int)
80
80
  reverse_iterator operator+(size_type)
81
81
  reverse_iterator operator-(size_type)
82
- difference_type operator-(iterator)
83
- difference_type operator-(const_iterator)
82
+ difference_type operator-(reverse_iterator)
83
+ difference_type operator-(const_reverse_iterator)
84
84
  bint operator==(reverse_iterator)
85
85
  bint operator==(const_reverse_iterator)
86
86
  bint operator!=(reverse_iterator)
@@ -104,8 +104,8 @@ cdef extern from "<vector>" namespace "std" nogil:
104
104
  const_reverse_iterator operator--(int)
105
105
  const_reverse_iterator operator+(size_type)
106
106
  const_reverse_iterator operator-(size_type)
107
- difference_type operator-(iterator)
108
- difference_type operator-(const_iterator)
107
+ difference_type operator-(reverse_iterator)
108
+ difference_type operator-(const_reverse_iterator)
109
109
  bint operator==(reverse_iterator)
110
110
  bint operator==(const_reverse_iterator)
111
111
  bint operator!=(reverse_iterator)