X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1a1a9ab
Drop unused requirements.txt
filmor Feb 16, 2026
4ca65d6
Bump astral-sh/setup-uv from 6 to 7 (#2656)
dependabot[bot] Oct 28, 2025
0856eb4
Bump actions/checkout from 5 to 6 (#2663)
dependabot[bot] Nov 24, 2025
a7d0842
Only init/shutdown Python once
filmor Oct 22, 2025
f6ff431
Disable NUnit analyzer for now
filmor Oct 23, 2025
9fc08f3
Reset conversions after each codec test
filmor Oct 23, 2025
a8e5020
Remove shutdown from most tests, disable the rest for now
filmor Oct 23, 2025
0a6062e
Use python -m pytest, path seemingly not properly updated
filmor Oct 26, 2025
6b1bb92
Remove unused architecture from uv env activation
filmor Oct 26, 2025
d4d1768
Synchronize the environment
filmor Oct 26, 2025
40a3db7
Include the probed PythonDLL value in the exception
filmor Dec 7, 2025
96f428f
Use the actual pytest runner
filmor Dec 7, 2025
5459ac7
Move tests that require reinit and only run on .NET Framework
filmor Dec 8, 2025
abb6855
Bump NUnit3TestAdapter from 5.2.0 to 6.0.0 (#2667)
dependabot[bot] Dec 8, 2025
9882788
Fix line endings
filmor Dec 9, 2025
dc5c6c4
Add previous commit to ignore file
filmor Dec 9, 2025
caac33d
Initial 3.14 commit
filmor Aug 9, 2025
e10d333
Apply alignment fix
filmor Oct 22, 2025
e976558
Disable problematic GC tests
filmor Oct 22, 2025
65af098
Set ht_token to NULL in Python 3.14
filmor Oct 24, 2025
e244503
Fix lockfile
filmor Oct 26, 2025
8e0333d
Assign True instead of None to __clear_reentry_guard__
filmor Dec 6, 2025
cd108b8
Disable the three remaining failing tests
filmor Dec 6, 2025
698bf00
Take the GIL in sequence and list wrappers
filmor Dec 7, 2025
908e13b
Move tp_clear workaround to .NET
filmor Dec 7, 2025
c851b3a
Skip coreclr embedded tests for now
filmor Dec 7, 2025
08550d0
Workaround for blocked PyObject_GenericSetAttr in metatypes
filmor Dec 7, 2025
f1d90f3
Revert changes to tests
filmor Dec 7, 2025
a47555d
Bump macos image version and add arm64
filmor Dec 8, 2025
cebfd15
Reenable .NET Core embedding tests
filmor Dec 9, 2025
8cb4b35
Merge pull request #2611 from pythonnet/python3.14
filmor Feb 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@ jobs:

- category: macos
platform: x64
instance: macos-13
instance: macos-14

python: ["3.10", "3.11", "3.12", "3.13"]
- category: macos
platform: arm64
instance: macos-14-arm64

python: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: Set Environment on macOS
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies = [
"clr_loader>=0.2.7,<0.3.0"
]

requires-python = ">=3.10, <3.14"
requires-python = ">=3.10, <3.15"

classifiers = [
"Development Status :: 5 - Production/Stable",
Expand Down
14 changes: 0 additions & 14 deletions requirements.txt

This file was deleted.

3 changes: 3 additions & 0 deletions src/runtime/CollectionWrappers/ListWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ public T this[int index]
{
get
{
using var _ = Py.GIL();
var item = Runtime.PyList_GetItem(pyObject, index);
var pyItem = new PyObject(item);
return pyItem.As<T>()!;
}
set
{
using var _ = Py.GIL();
var pyItem = value.ToPython();
var result = Runtime.PyList_SetItem(pyObject, index, new NewReference(pyItem).Steal());
if (result == -1)
Expand All @@ -37,6 +39,7 @@ public void Insert(int index, T item)
if (IsReadOnly)
throw new InvalidOperationException("Collection is read-only");

using var _ = Py.GIL();
var pyItem = item.ToPython();

int result = Runtime.PyList_Insert(pyObject, index, pyItem);
Expand Down
23 changes: 16 additions & 7 deletions src/runtime/CollectionWrappers/SequenceWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ public int Count
{
get
{
var size = Runtime.PySequence_Size(pyObject.Reference);
if (size == -1)
nint size = -1;
{
Runtime.CheckExceptionOccurred();
using var _ = Py.GIL();
size = Runtime.PySequence_Size(pyObject.Reference);
if (size == -1)
{
Runtime.CheckExceptionOccurred();
}
}

return checked((int)size);
Expand All @@ -38,6 +42,7 @@ public void Clear()
{
if (IsReadOnly)
throw new NotImplementedException();
using var _ = Py.GIL();
int result = Runtime.PySequence_DelSlice(pyObject, 0, Count);
if (result == -1)
{
Expand Down Expand Up @@ -77,12 +82,16 @@ protected bool removeAt(int index)
if (index >= Count || index < 0)
return false;

int result = Runtime.PySequence_DelItem(pyObject, index);

if (result == 0)
return true;
{
using var _ = Py.GIL();
int result = Runtime.PySequence_DelItem(pyObject, index);

if (result == 0)
return true;

Runtime.CheckExceptionOccurred();
Runtime.CheckExceptionOccurred();
}
return false;
}

Expand Down
6 changes: 3 additions & 3 deletions src/runtime/Native/PyIdentifier_.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ static class PyIdentifier
public static BorrowedReference __doc__ => new(f__doc__);
static IntPtr f__class__;
public static BorrowedReference __class__ => new(f__class__);
static IntPtr f__clear_reentry_guard__;
public static BorrowedReference __clear_reentry_guard__ => new(f__clear_reentry_guard__);
static IntPtr f__module__;
public static BorrowedReference __module__ => new(f__module__);
static IntPtr f__file__;
Expand All @@ -24,6 +22,8 @@ static class PyIdentifier
static IntPtr f__self__;
public static BorrowedReference __self__ => new(f__self__);
static IntPtr f__annotations__;
public static BorrowedReference __dictoffset__ => new(f__dictoffset__);
static IntPtr f__dictoffset__;
public static BorrowedReference __annotations__ => new(f__annotations__);
static IntPtr f__init__;
public static BorrowedReference __init__ => new(f__init__);
Expand Down Expand Up @@ -51,12 +51,12 @@ static partial class InternString
"__dict__",
"__doc__",
"__class__",
"__clear_reentry_guard__",
"__module__",
"__file__",
"__slots__",
"__self__",
"__annotations__",
"__dictoffset__",
"__init__",
"__repr__",
"__import__",
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Native/PyIdentifier_.tt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
"__dict__",
"__doc__",
"__class__",
"__clear_reentry_guard__",
"__module__",
"__file__",
"__slots__",
"__self__",
"__annotations__",
"__dictoffset__",

"__init__",
"__repr__",
Expand Down
15 changes: 15 additions & 0 deletions src/runtime/Native/TypeOffset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ static partial class TypeOffset
internal static int tp_setattro { get; private set; }
internal static int tp_str { get; private set; }
internal static int tp_traverse { get; private set; }
// Special case: Only available in Python 3.14 onwards, set to -1 by default
internal static int ht_token { get; private set; } = -1;

internal static void Use(ITypeOffsets offsets, int extraHeadOffset)
{
Expand All @@ -89,6 +91,19 @@ internal static void Use(ITypeOffsets offsets, int extraHeadOffset)
slotNames.Add(offsetProperty.Name);

var sourceProperty = typeof(ITypeOffsets).GetProperty(offsetProperty.Name);
if (sourceProperty == null)
{
if ((int)offsetProperty.GetValue(null) == -1)
{
// Skip, this is an optional offset value
continue;
}
else
{
throw new Exception($"No offset defined for necessary slot {offsetProperty.Name}");
}
}

int value = (int)sourceProperty.GetValue(offsets, null);
value += extraHeadOffset;
offsetProperty.SetValue(obj: null, value: value, index: null);
Expand Down
153 changes: 153 additions & 0 deletions src/runtime/Native/TypeOffset314.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@

// Auto-generated by geninterop.py.
// DO NOT MODIFY BY HAND.

// Python 3.14: ABI flags: ''

// ReSharper disable InconsistentNaming
// ReSharper disable IdentifierTypo

using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

using Python.Runtime.Native;

namespace Python.Runtime
{
[SuppressMessage("Style", "IDE1006:Naming Styles",
Justification = "Following CPython",
Scope = "type")]

[StructLayout(LayoutKind.Sequential)]
internal class TypeOffset314 : GeneratedTypeOffsets, ITypeOffsets
{
public TypeOffset314() { }
// Auto-generated from PyHeapTypeObject in Python.h
public int ob_refcnt_full { get; private set; }
public int ob_type { get; private set; }
public int ob_size { get; private set; }
public int tp_name { get; private set; }
public int tp_basicsize { get; private set; }
public int tp_itemsize { get; private set; }
public int tp_dealloc { get; private set; }
public int tp_vectorcall_offset { get; private set; }
public int tp_getattr { get; private set; }
public int tp_setattr { get; private set; }
public int tp_as_async { get; private set; }
public int tp_repr { get; private set; }
public int tp_as_number { get; private set; }
public int tp_as_sequence { get; private set; }
public int tp_as_mapping { get; private set; }
public int tp_hash { get; private set; }
public int tp_call { get; private set; }
public int tp_str { get; private set; }
public int tp_getattro { get; private set; }
public int tp_setattro { get; private set; }
public int tp_as_buffer { get; private set; }
public int tp_flags { get; private set; }
public int tp_doc { get; private set; }
public int tp_traverse { get; private set; }
public int tp_clear { get; private set; }
public int tp_richcompare { get; private set; }
public int tp_weaklistoffset { get; private set; }
public int tp_iter { get; private set; }
public int tp_iternext { get; private set; }
public int tp_methods { get; private set; }
public int tp_members { get; private set; }
public int tp_getset { get; private set; }
public int tp_base { get; private set; }
public int tp_dict { get; private set; }
public int tp_descr_get { get; private set; }
public int tp_descr_set { get; private set; }
public int tp_dictoffset { get; private set; }
public int tp_init { get; private set; }
public int tp_alloc { get; private set; }
public int tp_new { get; private set; }
public int tp_free { get; private set; }
public int tp_is_gc { get; private set; }
public int tp_bases { get; private set; }
public int tp_mro { get; private set; }
public int tp_cache { get; private set; }
public int tp_subclasses { get; private set; }
public int tp_weaklist { get; private set; }
public int tp_del { get; private set; }
public int tp_version_tag { get; private set; }
public int tp_finalize { get; private set; }
public int tp_vectorcall { get; private set; }
// This is an error in our generator:
//
// The fields below are actually not pointers (like we incorrectly
// assume for all other fields) but instead a char (1 byte) and a short
// (2 bytes). By dropping one of the fields, we still get the correct
// overall size of the struct.
public int tp_watched { get; private set; }
// public int tp_versions_used { get; private set; }
public int am_await { get; private set; }
public int am_aiter { get; private set; }
public int am_anext { get; private set; }
public int am_send { get; private set; }
public int nb_add { get; private set; }
public int nb_subtract { get; private set; }
public int nb_multiply { get; private set; }
public int nb_remainder { get; private set; }
public int nb_divmod { get; private set; }
public int nb_power { get; private set; }
public int nb_negative { get; private set; }
public int nb_positive { get; private set; }
public int nb_absolute { get; private set; }
public int nb_bool { get; private set; }
public int nb_invert { get; private set; }
public int nb_lshift { get; private set; }
public int nb_rshift { get; private set; }
public int nb_and { get; private set; }
public int nb_xor { get; private set; }
public int nb_or { get; private set; }
public int nb_int { get; private set; }
public int nb_reserved { get; private set; }
public int nb_float { get; private set; }
public int nb_inplace_add { get; private set; }
public int nb_inplace_subtract { get; private set; }
public int nb_inplace_multiply { get; private set; }
public int nb_inplace_remainder { get; private set; }
public int nb_inplace_power { get; private set; }
public int nb_inplace_lshift { get; private set; }
public int nb_inplace_rshift { get; private set; }
public int nb_inplace_and { get; private set; }
public int nb_inplace_xor { get; private set; }
public int nb_inplace_or { get; private set; }
public int nb_floor_divide { get; private set; }
public int nb_true_divide { get; private set; }
public int nb_inplace_floor_divide { get; private set; }
public int nb_inplace_true_divide { get; private set; }
public int nb_index { get; private set; }
public int nb_matrix_multiply { get; private set; }
public int nb_inplace_matrix_multiply { get; private set; }
public int mp_length { get; private set; }
public int mp_subscript { get; private set; }
public int mp_ass_subscript { get; private set; }
public int sq_length { get; private set; }
public int sq_concat { get; private set; }
public int sq_repeat { get; private set; }
public int sq_item { get; private set; }
public int was_sq_slice { get; private set; }
public int sq_ass_item { get; private set; }
public int was_sq_ass_slice { get; private set; }
public int sq_contains { get; private set; }
public int sq_inplace_concat { get; private set; }
public int sq_inplace_repeat { get; private set; }
public int bf_getbuffer { get; private set; }
public int bf_releasebuffer { get; private set; }
public int name { get; private set; }
public int ht_slots { get; private set; }
public int qualname { get; private set; }
public int ht_cached_keys { get; private set; }
public int ht_module { get; private set; }
public int _ht_tpname { get; private set; }
public int ht_token { get; private set; }
public int spec_cache_getitem { get; private set; }
public int getitem_version { get; private set; }
public int init { get; private set; }
}
}

2 changes: 1 addition & 1 deletion src/runtime/PythonEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public static string PythonPath
}

public static Version MinSupportedVersion => new(3, 7);
public static Version MaxSupportedVersion => new(3, 13, int.MaxValue, int.MaxValue);
public static Version MaxSupportedVersion => new(3, 14, int.MaxValue, int.MaxValue);
public static bool IsSupportedVersion(Version version) => version >= MinSupportedVersion && version <= MaxSupportedVersion;

public static string Version
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,11 @@ internal static PyType AllocateTypeObject(string name, PyType metatype)
Util.WriteIntPtr(type, TypeOffset.tp_traverse, subtype_traverse);
Util.WriteIntPtr(type, TypeOffset.tp_clear, subtype_clear);

// This is a new mechanism in Python 3.14. We should eventually use it to implement
// a nicer type check, but for now we just need to ensure that it is set to NULL.
if (TypeOffset.ht_token != -1)
Util.WriteIntPtr(type, TypeOffset.ht_token, IntPtr.Zero);

InheritSubstructs(type.Reference.DangerousGetAddress());

return type;
Expand Down
21 changes: 11 additions & 10 deletions src/runtime/Types/ClassBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ public static int tp_clear(BorrowedReference ob)
return 0;
}

static readonly HashSet<IntPtr> ClearVisited = new();

internal static unsafe int BaseUnmanagedClear(BorrowedReference ob)
{
var type = Runtime.PyObject_TYPE(ob);
Expand All @@ -385,21 +387,20 @@ internal static unsafe int BaseUnmanagedClear(BorrowedReference ob)
}
var clear = (delegate* unmanaged[Cdecl]<BorrowedReference, int>)clearPtr;

bool usesSubtypeClear = clearPtr == TypeManager.subtype_clear;
if (usesSubtypeClear)
if (clearPtr == TypeManager.subtype_clear)
{
// workaround for https://bugs.python.org/issue45266 (subtype_clear)
using var dict = Runtime.PyObject_GenericGetDict(ob);
if (Runtime.PyMapping_HasKey(dict.Borrow(), PyIdentifier.__clear_reentry_guard__) != 0)
var addr = ob.DangerousGetAddress();
if (!ClearVisited.Add(addr))
return 0;
int res = Runtime.PyDict_SetItem(dict.Borrow(), PyIdentifier.__clear_reentry_guard__, Runtime.None);
if (res != 0) return res;

res = clear(ob);
Runtime.PyDict_DelItem(dict.Borrow(), PyIdentifier.__clear_reentry_guard__);
int res = clear(ob);
ClearVisited.Remove(addr);
return res;
}
return clear(ob);
else
{
return clear(ob);
}
}

protected override Dictionary<string, object?> OnSave(BorrowedReference ob)
Expand Down
Loading
Loading
X Tutup