diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 000000000..d7b97b3a7
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,2 @@
+# Line endings normalization
+fd7c7e1cbd8e1d7bdb2ec2423c3cf9f95a3abed1
diff --git a/.github/actions/install-mono/action.yml b/.github/actions/install-mono/action.yml
new file mode 100644
index 000000000..f414afdc7
--- /dev/null
+++ b/.github/actions/install-mono/action.yml
@@ -0,0 +1,78 @@
+name: 'Install Mono'
+description: 'Install Mono'
+branding:
+ icon: package
+ color: blue
+inputs:
+ arch:
+ description: Architecture to install for
+ required: true
+runs:
+ using: "composite"
+ steps:
+ # Windows Cache
+ - name: Cache setup on Windows
+ if: runner.os == 'Windows'
+ run: |
+ mkdir -p .choco-cache
+ choco config set cacheLocation $(pwd)/.choco-cache
+ shell: bash
+
+ - name: Cache on Windows
+ if: runner.os == 'Windows'
+ uses: actions/cache@v5
+ with:
+ path: .choco-cache
+ key: mono-${{ runner.os }}-${{ inputs.arch }}
+
+ # macOS Cache
+ - name: Set Homebrew Cache Path
+ if: runner.os == 'macOS'
+ run: |
+ mkdir -p .brew-cache
+ echo "HOMEBREW_CACHE=$(pwd)/.brew-cache" >> $GITHUB_ENV
+ shell: bash
+
+ - name: Cache Homebrew on macOS
+ if: runner.os == 'macOS'
+ uses: actions/cache@v5
+ with:
+ path: .brew-cache
+ key: mono-brew-${{ runner.os }}-${{ inputs.arch }}
+
+ # ===================================
+
+ - name: Install on Linux
+ if: runner.os == 'Linux'
+ uses: awalsh128/cache-apt-pkgs-action@v1
+ with:
+ packages: mono-runtime-sgen
+
+ # ===================================
+
+ - name: Install on Windows (x86)
+ if: runner.os == 'Windows' && inputs.arch == 'x86'
+ run: choco install --x86 -y mono
+ shell: sh
+
+ - name: Install on Windows
+ if: runner.os == 'Windows' && inputs.arch != 'x86'
+ run: choco install -y mono
+ shell: sh
+
+ # ===================================
+ #
+ - name: Install on macOS (x86_64)
+ if: runner.os == 'macOS' && inputs.arch == 'x64'
+ run: |
+ arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+ arch -x86_64 /usr/local/bin/brew install --ignore-dependencies mono
+ shell: sh
+
+ - name: Install on macOS (arm64)
+ if: runner.os == 'macOS' && inputs.arch != 'x64'
+ run: |
+ brew update
+ brew install --ignore-dependencies mono
+ shell: sh
+
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..47db1e621
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,16 @@
+version: 2
+updates:
+ - package-ecosystem: "pip"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "nuget"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/ARM.yml b/.github/workflows/ARM.yml
deleted file mode 100644
index eef0e666d..000000000
--- a/.github/workflows/ARM.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-name: Main (ARM)
-
-on:
- push:
- branches:
- - master
- pull_request:
-
-jobs:
- build-test-arm:
- name: Build and Test ARM64
- runs-on: [self-hosted, linux, ARM64]
- timeout-minutes: 15
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v2
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: '6.0.x'
-
- - name: Clean previous install
- run: |
- pip uninstall -y pythonnet
-
- - name: Install dependencies
- run: |
- pip3.8 install -r requirements.txt
- pip3.8 install pytest numpy # for tests
-
- - name: Build and Install
- run: |
- pip3.8 install -v .
-
- - name: Set Python DLL path (non Windows)
- run: |
- echo PYTHONNET_PYDLL=$(python3.8 -m find_libpython) >> $GITHUB_ENV
-
- - name: Embedding tests
- run: dotnet test --logger "console;verbosity=detailed" src/embed_tests/
-
- - name: Python Tests (Mono)
- run: python3.8 -m pytest --runtime mono
-
- - name: Python Tests (.NET Core)
- run: python3.8 -m pytest --runtime coreclr
-
- - name: Python tests run from .NET
- run: dotnet test src/python_tests_runner/
-
- #- name: Perf tests
- # run: |
- # pip install --force --no-deps --target src/perf_tests/baseline/ pythonnet==2.5.2
- # dotnet test --configuration Release --logger "console;verbosity=detailed" src/perf_tests/
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 30163cd14..3937d85e0 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -6,7 +6,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Doxygen Action
uses: mattnotmitt/doxygen-action@1.12.0
with:
@@ -19,7 +19,7 @@ jobs:
- name: Upload artifact
# Automatically uploads an artifact from the './_site' directory by default
- uses: actions/upload-pages-artifact@v3
+ uses: actions/upload-pages-artifact@v4
with:
path: doc/build/html/
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 8485189e1..b2fd96863 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -19,98 +19,120 @@ jobs:
- category: windows
platform: x86
instance: windows-latest
+ suffix: -windows-x86-none
- category: windows
platform: x64
instance: windows-latest
+ suffix: -windows-x86_64-none
- category: ubuntu
platform: x64
instance: ubuntu-22.04
+ suffix: ""
+
+ - category: ubuntu
+ platform: arm64
+ instance: ubuntu-22.04-arm
+ suffix: ""
- category: macos
platform: x64
- instance: macos-13
+ instance: macos-15
+ suffix: -macos-x86_64-none
+
+ - category: macos
+ platform: arm64
+ instance: macos-15
+ suffix: -macos-aarch64-none
- python: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
+ python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
- # This fails in pytest with:
- # CSC : error CS4023: /platform:anycpu32bitpreferred can only be used with /t:exe, /t:winexe and /t:appcontainerexe [D:\a\pythonnet\pythonnet\src\runtime\Python.Runtime.csproj]
exclude:
- - os:
+ # Fails with initfs_encoding error
+ - os:
category: windows
platform: x86
- python: "3.13"
+ python: "3.10"
+
+ # Broken ctypes find_library
+ - os:
+ category: macos
+ platform: arm64
+ python: '3.10'
+ - os:
+ category: macos
+ platform: arm64
+ python: '3.11'
+ - os:
+ category: macos
+ platform: arm64
+ python: '3.12'
+
+ # Fails to find pytest
+ - os:
+ category: windows
+ platform: x64
+ python: '3.10'
- steps:
- - name: Set Environment on macOS
- uses: maxim-lobanov/setup-xamarin@v1
- if: ${{ matrix.os.category == 'macos' }}
- with:
- mono-version: latest
+ # fails to call mono methods
+ - os:
+ category: windows
+ platform: x86
+ python: '3.13'
+ env:
+ NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
+ steps:
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v6
+ # Use main until support for architecture lands
- name: Setup .NET
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: '6.0.x'
-
- - name: Set up Python ${{ matrix.python }}
- uses: actions/setup-python@v2
+ uses: actions/setup-dotnet@main
with:
- python-version: ${{ matrix.python }}
+ dotnet-version: '10.0'
architecture: ${{ matrix.os.platform }}
- - name: Install dependencies
- run: |
- pip install --upgrade -r requirements.txt
- pip install numpy # for tests
+ - run: dotnet restore
- - name: Build and Install
- run: |
- pip install -v .
+ - name: Install Mono
+ uses: ./.github/actions/install-mono
+ with:
+ arch: ${{ matrix.os.platform }}
- - name: Set Python DLL path and PYTHONHOME (non Windows)
- if: ${{ matrix.os.category != 'windows' }}
- run: |
- echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV
- echo PYTHONHOME=$(python -c 'import sys; print(sys.prefix)') >> $GITHUB_ENV
+ - name: Set up Python ${{ matrix.python }}
+ uses: astral-sh/setup-uv@v7
+ with:
+ python-version: cpython-${{ matrix.python }}${{ matrix.os.suffix }}
+ cache-python: true
+ activate-environment: true
+ enable-cache: true
- - name: Set Python DLL path and PYTHONHOME (Windows)
- if: ${{ matrix.os.category == 'windows' }}
- run: |
- Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append -InputObject "PYTHONNET_PYDLL=$(python -m find_libpython)"
- Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append -InputObject "PYTHONHOME=$(python -c 'import sys; print(sys.prefix)')"
+ - name: Synchronize the virtual environment
+ run: uv sync --managed-python
- - name: Embedding tests
- if: ${{ matrix.python != '3.13' }}
- run: dotnet test --runtime any-${{ matrix.os.platform }} --logger "console;verbosity=detailed" src/embed_tests/
+ - name: Embedding tests (Mono/.NET Framework)
+ if: always()
+ run: dotnet test --runtime any-${{ matrix.os.platform }} --framework net472 --logger "console;verbosity=detailed" src/embed_tests/
env:
MONO_THREADS_SUSPEND: preemptive # https://github.com/mono/mono/issues/21466
- - name: Python Tests (Mono)
- if: ${{ matrix.os.category != 'windows' }}
- run: pytest --runtime mono
+ - name: Embedding tests (.NET Core)
+ if: always()
+ run: dotnet test --runtime any-${{ matrix.os.platform }} --framework net10.0 --logger "console;verbosity=detailed" src/embed_tests/
- # TODO: Run these tests on Windows x86
- name: Python Tests (.NET Core)
- if: ${{ matrix.os.platform == 'x64' }}
+ if: always()
run: pytest --runtime coreclr
+ - name: Python Tests (Mono)
+ if: always()
+ run: pytest --runtime mono
+
- name: Python Tests (.NET Framework)
if: ${{ matrix.os.category == 'windows' }}
run: pytest --runtime netfx
- name: Python tests run from .NET
- if: ${{ matrix.python != '3.13' }}
run: dotnet test --runtime any-${{ matrix.os.platform }} src/python_tests_runner/
-
- - name: Perf tests
- if: ${{ (matrix.python == '3.8') && (matrix.os.platform == 'x64') }}
- run: |
- pip install --force --no-deps --target src/perf_tests/baseline/ pythonnet==2.5.2
- dotnet test --configuration Release --runtime any-${{ matrix.os.platform }} --logger "console;verbosity=detailed" src/perf_tests/
-
- # TODO: Run mono tests on Windows?
diff --git a/.github/workflows/nuget-preview.yml b/.github/workflows/nuget-preview.yml
index d652f4b1e..6de97d50e 100644
--- a/.github/workflows/nuget-preview.yml
+++ b/.github/workflows/nuget-preview.yml
@@ -21,15 +21,15 @@ jobs:
echo "DATE_VER=$(date "+%Y-%m-%d")" >> $GITHUB_ENV
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v6
- name: Setup .NET
- uses: actions/setup-dotnet@v1
+ uses: actions/setup-dotnet@v5
with:
dotnet-version: '6.0.x'
- name: Set up Python 3.8
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v6
with:
python-version: 3.8
architecture: x64
diff --git a/Directory.Build.props b/Directory.Build.props
index e45c16f6a..9b6f9555a 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,24 +1,25 @@
- Copyright (c) 2006-2022 The Contributors of the Python.NET Project
+ Copyright (c) 2006-2025 The Contributors of the Python.NET Project
pythonnet
Python.NET
- 10.0
+ 12.0
false
$([System.IO.File]::ReadAllText("$(MSBuildThisFileDirectory)version.txt").Trim())
$(FullVersion.Split('-', 2)[0])
$(FullVersion.Split('-', 2)[1])
+ $(MSBuildThisFileDirectory)
-
-
+
+
all
runtime; build; native; contentfiles; analyzers
-
+
diff --git a/doc/make.bat b/doc/make.bat
index 747ffb7b3..dc1312ab0 100644
--- a/doc/make.bat
+++ b/doc/make.bat
@@ -1,35 +1,35 @@
-@ECHO OFF
-
-pushd %~dp0
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set SOURCEDIR=source
-set BUILDDIR=build
-
-%SPHINXBUILD% >NUL 2>NUL
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.https://www.sphinx-doc.org/
- exit /b 1
-)
-
-if "%1" == "" goto help
-
-%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-goto end
-
-:help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-
-:end
-popd
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.https://www.sphinx-doc.org/
+ exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/pyproject.toml b/pyproject.toml
index 968998e8d..59d4d107a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,33 +1,30 @@
[build-system]
-requires = ["setuptools>=61", "wheel"]
+requires = ["setuptools>=80"]
build-backend = "setuptools.build_meta"
[project]
name = "pythonnet"
description = ".NET and Mono integration for Python"
-license = {text = "MIT"}
+license = "MIT"
readme = "README.rst"
dependencies = [
- "clr_loader>=0.2.7,<0.3.0"
+ "clr_loader>=0.3.0,<0.4.0"
]
-requires-python = ">=3.7, <3.14"
+requires-python = ">=3.10, <3.15"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
- "License :: OSI Approved :: MIT License",
"Programming Language :: C#",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.7",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Operating System :: MacOS :: MacOS X",
@@ -38,7 +35,7 @@ dynamic = ["version"]
[dependency-groups]
dev = [
"pytest >= 6",
- "find_libpython >= 0.3.0",
+ "find_libpython >= 0.3",
"numpy >=2 ; python_version >= '3.10'",
"numpy <2 ; python_version < '3.10'",
"psutil"
@@ -55,7 +52,6 @@ Sources = "https://github.com/pythonnet/pythonnet"
[tool.setuptools]
zip-safe = false
py-modules = ["clr"]
-license-files = []
[tool.setuptools.dynamic.version]
file = "version.txt"
diff --git a/pythonnet.sln b/pythonnet.sln
index cf684c0e1..9dfeb44b1 100644
--- a/pythonnet.sln
+++ b/pythonnet.sln
@@ -8,8 +8,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.EmbeddingTest", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Test", "src\testing\Python.Test.csproj", "{14EF9518-5BB7-4F83-8686-015BD2CC788E}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.PerformanceTests", "src\perf_tests\Python.PerformanceTests.csproj", "{4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.DomainReloadTests", "tests\domain_tests\Python.DomainReloadTests.csproj", "{F2FB6DA3-318E-4F30-9A1F-932C667E38C5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F4C6-4EE4-9AEE-315FD79BE2D5}"
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 33f38cca7..000000000
--- a/requirements.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-# Requirements for both Travis and AppVeyor
-pytest
-psutil
-
-# Coverage upload
-coverage
-codecov
-
-wheel
-pycparser
-clr-loader==0.2.*
-
-# Discover libpython
-find_libpython==0.3.*
diff --git a/src/embed_tests/CallableObject.cs b/src/embed_tests/CallableObject.cs
index 8466f5ad8..d450598d2 100644
--- a/src/embed_tests/CallableObject.cs
+++ b/src/embed_tests/CallableObject.cs
@@ -9,34 +9,37 @@ namespace Python.EmbeddingTest
{
public class CallableObject
{
+ IPythonBaseTypeProvider BaseTypeProvider;
+
[OneTimeSetUp]
public void SetUp()
{
- PythonEngine.Initialize();
using var locals = new PyDict();
PythonEngine.Exec(CallViaInheritance.BaseClassSource, locals: locals);
- CustomBaseTypeProvider.BaseClass = new PyType(locals[CallViaInheritance.BaseClassName]);
- PythonEngine.InteropConfiguration.PythonBaseTypeProviders.Add(new CustomBaseTypeProvider());
+ BaseTypeProvider = new CustomBaseTypeProvider(new PyType(locals[CallViaInheritance.BaseClassName]));
+ PythonEngine.InteropConfiguration.PythonBaseTypeProviders.Add(BaseTypeProvider);
}
[OneTimeTearDown]
public void Dispose()
{
- PythonEngine.Shutdown();
+ PythonEngine.InteropConfiguration.PythonBaseTypeProviders.Remove(BaseTypeProvider);
}
+
[Test]
public void CallMethodMakesObjectCallable()
{
var doubler = new DerivedDoubler();
dynamic applyObjectTo21 = PythonEngine.Eval("lambda o: o(21)");
- Assert.AreEqual(doubler.__call__(21), (int)applyObjectTo21(doubler.ToPython()));
+ Assert.That((int)applyObjectTo21(doubler.ToPython()), Is.EqualTo(doubler.__call__(21)));
}
+
[Test]
public void CallMethodCanBeInheritedFromPython()
{
var callViaInheritance = new CallViaInheritance();
dynamic applyObjectTo14 = PythonEngine.Eval("lambda o: o(14)");
- Assert.AreEqual(callViaInheritance.Call(14), (int)applyObjectTo14(callViaInheritance.ToPython()));
+ Assert.That((int)applyObjectTo14(callViaInheritance.ToPython()), Is.EqualTo(callViaInheritance.Call(14)));
}
[Test]
@@ -48,7 +51,7 @@ public void CanOverwriteCall()
scope.Exec("orig_call = o.Call");
scope.Exec("o.Call = lambda a: orig_call(a*7)");
int result = scope.Eval("o.Call(5)");
- Assert.AreEqual(105, result);
+ Assert.That(result, Is.EqualTo(105));
}
class Doubler
@@ -71,16 +74,14 @@ class {BaseClassName}(MyCallableBase): pass
public int Call(int arg) => 3 * arg;
}
- class CustomBaseTypeProvider : IPythonBaseTypeProvider
+ class CustomBaseTypeProvider(PyType BaseClass) : IPythonBaseTypeProvider
{
- internal static PyType BaseClass;
-
public IEnumerable GetBaseTypes(Type type, IList existingBases)
{
- Assert.Greater(BaseClass.Refcount, 0);
+ Assert.That(BaseClass.Refcount, Is.GreaterThan(0));
return type != typeof(CallViaInheritance)
? existingBases
- : new[] { BaseClass };
+ : [BaseClass];
}
}
}
diff --git a/src/embed_tests/ClassManagerTests.cs b/src/embed_tests/ClassManagerTests.cs
index 72025a28b..83bfa7bc2 100644
--- a/src/embed_tests/ClassManagerTests.cs
+++ b/src/embed_tests/ClassManagerTests.cs
@@ -6,18 +6,6 @@ namespace Python.EmbeddingTest
{
public class ClassManagerTests
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void NestedClassDerivingFromParent()
{
diff --git a/src/embed_tests/CodecGroups.cs b/src/embed_tests/CodecGroups.cs
index 689e5b24c..22ed0df72 100644
--- a/src/embed_tests/CodecGroups.cs
+++ b/src/embed_tests/CodecGroups.cs
@@ -20,7 +20,7 @@ public void GetEncodersByType()
};
var got = group.GetEncoders(typeof(Uri)).ToArray();
- CollectionAssert.AreEqual(new[]{encoder1, encoder2}, got);
+ Assert.That(got, Is.EqualTo(new[] { encoder1, encoder2 }).AsCollection);
}
[Test]
@@ -31,9 +31,13 @@ public void CanEncode()
new ObjectToEncoderInstanceEncoder(),
};
- Assert.IsTrue(group.CanEncode(typeof(Tuple)));
- Assert.IsTrue(group.CanEncode(typeof(Uri)));
- Assert.IsFalse(group.CanEncode(typeof(string)));
+ Assert.Multiple(() =>
+ {
+ Assert.That(group.CanEncode(typeof(Tuple)), Is.True);
+ Assert.That(group.CanEncode(typeof(Uri)), Is.True);
+ Assert.That(group.CanEncode(typeof(string)), Is.False);
+ });
+
}
[Test]
@@ -50,12 +54,12 @@ public void Encodes()
var uri = group.TryEncode(new Uri("data:"));
var clrObject = (CLRObject)ManagedType.GetManagedObject(uri);
- Assert.AreSame(encoder1, clrObject.inst);
- Assert.AreNotSame(encoder2, clrObject.inst);
+ Assert.That(clrObject.inst, Is.SameAs(encoder1));
+ Assert.That(clrObject.inst, Is.Not.SameAs(encoder2));
var tuple = group.TryEncode(Tuple.Create(1));
clrObject = (CLRObject)ManagedType.GetManagedObject(tuple);
- Assert.AreSame(encoder0, clrObject.inst);
+ Assert.That(clrObject.inst, Is.SameAs(encoder0));
}
[Test]
@@ -72,11 +76,11 @@ public void GetDecodersByTypes()
};
var decoder = group.GetDecoder(pyfloat, typeof(string));
- Assert.AreSame(decoder2, decoder);
+ Assert.That(decoder, Is.SameAs(decoder2));
decoder = group.GetDecoder(pystr, typeof(string));
- Assert.IsNull(decoder);
+ Assert.That(decoder, Is.Null);
decoder = group.GetDecoder(pyint, typeof(long));
- Assert.AreSame(decoder1, decoder);
+ Assert.That(decoder, Is.SameAs(decoder1));
}
[Test]
public void CanDecode()
@@ -91,10 +95,14 @@ public void CanDecode()
decoder2,
};
- Assert.IsTrue(group.CanDecode(pyint, typeof(long)));
- Assert.IsFalse(group.CanDecode(pyint, typeof(int)));
- Assert.IsTrue(group.CanDecode(pyfloat, typeof(string)));
- Assert.IsFalse(group.CanDecode(pystr, typeof(string)));
+ Assert.Multiple(() =>
+ {
+ Assert.That(group.CanDecode(pyint, typeof(long)));
+ Assert.That(group.CanDecode(pyint, typeof(int)), Is.False);
+ Assert.That(group.CanDecode(pyfloat, typeof(string)));
+ Assert.That(group.CanDecode(pystr, typeof(string)), Is.False);
+ });
+
}
[Test]
@@ -109,24 +117,14 @@ public void Decodes()
decoder2,
};
- Assert.IsTrue(group.TryDecode(new PyInt(10), out long longResult));
- Assert.AreEqual(42, longResult);
- Assert.IsTrue(group.TryDecode(new PyFloat(10), out string strResult));
- Assert.AreSame("atad:", strResult);
-
- Assert.IsFalse(group.TryDecode(new PyInt(10), out int _));
- }
-
- [SetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [TearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
+ Assert.Multiple(() =>
+ {
+ Assert.That(group.TryDecode(new PyInt(10), out long longResult));
+ Assert.That(longResult, Is.EqualTo(42));
+ Assert.That(group.TryDecode(new PyFloat(10), out string strResult));
+ Assert.That(strResult, Is.SameAs("atad:"));
+ Assert.That(group.TryDecode(new PyInt(10), out int _), Is.False);
+ });
}
}
}
diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs
index c8b8ecb6e..5879462f5 100644
--- a/src/embed_tests/Codecs.cs
+++ b/src/embed_tests/Codecs.cs
@@ -8,16 +8,10 @@ namespace Python.EmbeddingTest {
public class Codecs
{
- [SetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
[TearDown]
- public void Dispose()
+ public void TearDown()
{
- PythonEngine.Shutdown();
+ PyObjectConversions.Reset();
}
[Test]
@@ -286,6 +280,7 @@ public void IterableDecoderTest()
// regression for https://github.com/pythonnet/pythonnet/issues/1427
[Test]
+ [Ignore("Broken, the list_encoder object ends up in builtins and fails during GC")]
public void PythonRegisteredDecoder_NoStackOverflowOnSystemType()
{
const string PyCode = @"
diff --git a/src/embed_tests/dynamic.cs b/src/embed_tests/Dynamic.cs
similarity index 95%
rename from src/embed_tests/dynamic.cs
rename to src/embed_tests/Dynamic.cs
index 6e3bfc4cb..174167118 100644
--- a/src/embed_tests/dynamic.cs
+++ b/src/embed_tests/Dynamic.cs
@@ -8,18 +8,6 @@ namespace Python.EmbeddingTest
{
public class DynamicTest
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
///
/// Set the attribute of a PyObject with a .NET object.
///
diff --git a/src/embed_tests/Events.cs b/src/embed_tests/Events.cs
index c216f4214..94a30726b 100644
--- a/src/embed_tests/Events.cs
+++ b/src/embed_tests/Events.cs
@@ -10,18 +10,6 @@ namespace Python.EmbeddingTest;
public class Events
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void UsingDoesNotLeak()
{
diff --git a/src/embed_tests/ExtensionTypes.cs b/src/embed_tests/ExtensionTypes.cs
index 803845960..3e8ead142 100644
--- a/src/embed_tests/ExtensionTypes.cs
+++ b/src/embed_tests/ExtensionTypes.cs
@@ -8,18 +8,6 @@ namespace Python.EmbeddingTest;
public class ExtensionTypes
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void WeakrefIsNone_AfterBoundMethodIsGone()
{
@@ -27,6 +15,6 @@ public void WeakrefIsNone_AfterBoundMethodIsGone()
var boundMethod = new UriBuilder().ToPython().GetAttr(nameof(UriBuilder.GetHashCode));
var weakref = makeref.Invoke(boundMethod);
boundMethod.Dispose();
- Assert.IsTrue(weakref.Invoke().IsNone());
+ Assert.That(weakref.Invoke().IsNone(), Is.True);
}
}
diff --git a/src/embed_tests/GlobalTestsSetup.cs b/src/embed_tests/GlobalTestsSetup.cs
index dff58b978..4f681dd9f 100644
--- a/src/embed_tests/GlobalTestsSetup.cs
+++ b/src/embed_tests/GlobalTestsSetup.cs
@@ -13,6 +13,7 @@ public partial class GlobalTestsSetup
public void GlobalSetup()
{
Finalizer.Instance.ErrorHandler += FinalizerErrorHandler;
+ PythonEngine.Initialize();
}
private void FinalizerErrorHandler(object sender, Finalizer.ErrorArgs e)
diff --git a/src/embed_tests/Inheritance.cs b/src/embed_tests/Inheritance.cs
index ebbc24dc4..1074fa288 100644
--- a/src/embed_tests/Inheritance.cs
+++ b/src/embed_tests/Inheritance.cs
@@ -9,23 +9,31 @@ namespace Python.EmbeddingTest
{
public class Inheritance
{
+ ExtraBaseTypeProvider ExtraBaseTypeProvider;
+ NoEffectBaseTypeProvider NoEffectBaseTypeProvider;
+
+
[OneTimeSetUp]
public void SetUp()
{
- PythonEngine.Initialize();
using var locals = new PyDict();
PythonEngine.Exec(InheritanceTestBaseClassWrapper.ClassSourceCode, locals: locals);
- ExtraBaseTypeProvider.ExtraBase = new PyType(locals[InheritanceTestBaseClassWrapper.ClassName]);
+
+ NoEffectBaseTypeProvider = new NoEffectBaseTypeProvider();
+ ExtraBaseTypeProvider = new ExtraBaseTypeProvider(new PyType(locals[InheritanceTestBaseClassWrapper.ClassName]));
+
var baseTypeProviders = PythonEngine.InteropConfiguration.PythonBaseTypeProviders;
- baseTypeProviders.Add(new ExtraBaseTypeProvider());
- baseTypeProviders.Add(new NoEffectBaseTypeProvider());
+ baseTypeProviders.Add(ExtraBaseTypeProvider);
+ baseTypeProviders.Add(NoEffectBaseTypeProvider);
}
[OneTimeTearDown]
public void Dispose()
{
- ExtraBaseTypeProvider.ExtraBase.Dispose();
- PythonEngine.Shutdown();
+ var baseTypeProviders = PythonEngine.InteropConfiguration.PythonBaseTypeProviders;
+ baseTypeProviders.Remove(NoEffectBaseTypeProvider);
+ baseTypeProviders.Remove(ExtraBaseTypeProvider);
+ ExtraBaseTypeProvider.Dispose();
}
[Test]
@@ -33,7 +41,7 @@ public void ExtraBase_PassesInstanceCheck()
{
var inherited = new Inherited();
bool properlyInherited = PyIsInstance(inherited, ExtraBaseTypeProvider.ExtraBase);
- Assert.IsTrue(properlyInherited);
+ Assert.That(properlyInherited, Is.True);
}
static dynamic PyIsInstance => PythonEngine.Eval("isinstance");
@@ -44,7 +52,7 @@ public void InheritingWithExtraBase_CreatesNewClass()
PyObject a = ExtraBaseTypeProvider.ExtraBase;
var inherited = new Inherited();
PyObject inheritedClass = inherited.ToPython().GetAttr("__class__");
- Assert.IsFalse(PythonReferenceComparer.Instance.Equals(a, inheritedClass));
+ Assert.That(PythonReferenceComparer.Instance.Equals(a, inheritedClass), Is.False);
}
[Test]
@@ -56,7 +64,7 @@ public void InheritedFromInheritedClassIsSelf()
PyObject b = scope.Eval("B");
PyObject bInstance = b.Invoke();
PyObject bInstanceClass = bInstance.GetAttr("__class__");
- Assert.IsTrue(PythonReferenceComparer.Instance.Equals(b, bInstanceClass));
+ Assert.That(PythonReferenceComparer.Instance.Equals(b, bInstanceClass), Is.True);
}
// https://github.com/pythonnet/pythonnet/issues/1420
@@ -76,7 +84,7 @@ public void Grandchild_PassesExtraBaseInstanceCheck()
PyObject b = scope.Eval("B");
PyObject bInst = b.Invoke();
bool properlyInherited = PyIsInstance(bInst, ExtraBaseTypeProvider.ExtraBase);
- Assert.IsTrue(properlyInherited);
+ Assert.That(properlyInherited, Is.True);
}
[Test]
@@ -84,7 +92,7 @@ public void CallInheritedClrMethod_WithExtraPythonBase()
{
var instance = new Inherited().ToPython();
string result = instance.InvokeMethod(nameof(PythonWrapperBase.WrapperBaseMethod)).As();
- Assert.AreEqual(result, nameof(PythonWrapperBase.WrapperBaseMethod));
+ Assert.That(nameof(PythonWrapperBase.WrapperBaseMethod), Is.EqualTo(result));
}
[Test]
@@ -94,7 +102,7 @@ public void CallExtraBaseMethod()
using var scope = Py.CreateScope();
scope.Set(nameof(instance), instance);
int actual = instance.ToPython().InvokeMethod("callVirt").As();
- Assert.AreEqual(expected: Inherited.OverridenVirtValue, actual);
+ Assert.That(actual, Is.EqualTo(Inherited.OverridenVirtValue));
}
[Test]
@@ -105,7 +113,7 @@ public void SetAdHocAttributes_WhenExtraBasePresent()
scope.Set(nameof(instance), instance);
scope.Exec($"super({nameof(instance)}.__class__, {nameof(instance)}).set_x_to_42()");
int actual = scope.Eval($"{nameof(instance)}.{nameof(Inherited.XProp)}");
- Assert.AreEqual(expected: Inherited.X, actual);
+ Assert.That(actual, Is.EqualTo(Inherited.X));
}
// https://github.com/pythonnet/pythonnet/issues/1476
@@ -115,9 +123,9 @@ public void BaseClearIsCalled()
using var scope = Py.CreateScope();
scope.Set("exn", new Exception("42"));
var msg = scope.Eval("exn.args[0]");
- Assert.AreEqual(2, msg.Refcount);
+ Assert.That(msg.Refcount, Is.EqualTo(2));
scope.Set("exn", null);
- Assert.AreEqual(1, msg.Refcount);
+ Assert.That(msg.Refcount, Is.EqualTo(1));
}
// https://github.com/pythonnet/pythonnet/issues/1455
@@ -126,18 +134,24 @@ public void PropertyAccessorOverridden()
{
using var derived = new PropertyAccessorDerived().ToPython();
derived.SetAttr(nameof(PropertyAccessorDerived.VirtualProp), "hi".ToPython());
- Assert.AreEqual("HI", derived.GetAttr(nameof(PropertyAccessorDerived.VirtualProp)).As());
+ Assert.That(derived.GetAttr(nameof(PropertyAccessorDerived.VirtualProp)).As(), Is.EqualTo("HI"));
}
}
- class ExtraBaseTypeProvider : IPythonBaseTypeProvider
+ class ExtraBaseTypeProvider(PyType ExtraBase) : IPythonBaseTypeProvider, IDisposable
{
- internal static PyType ExtraBase;
+ public PyType ExtraBase { get; } = ExtraBase;
+
+ public void Dispose()
+ {
+ ExtraBase.Dispose();
+ }
+
public IEnumerable GetBaseTypes(Type type, IList existingBases)
{
if (type == typeof(InheritanceTestBaseClassWrapper))
{
- return new[] { PyType.Get(type.BaseType), ExtraBase };
+ return [PyType.Get(type.BaseType), ExtraBase];
}
return existingBases;
}
diff --git a/src/embed_tests/Inspect.cs b/src/embed_tests/Inspect.cs
index 8ff94e02c..0c4ce43f3 100644
--- a/src/embed_tests/Inspect.cs
+++ b/src/embed_tests/Inspect.cs
@@ -9,18 +9,6 @@ namespace Python.EmbeddingTest
{
public class Inspect
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void InstancePropertiesVisibleOnClass()
{
@@ -28,7 +16,7 @@ public void InstancePropertiesVisibleOnClass()
var uriClass = uri.GetPythonType();
var property = uriClass.GetAttr(nameof(Uri.AbsoluteUri));
var pyProp = (PropertyObject)ManagedType.GetManagedObject(property.Reference);
- Assert.AreEqual(nameof(Uri.AbsoluteUri), pyProp.info.Value.Name);
+ Assert.That(pyProp.info.Value.Name, Is.EqualTo(nameof(Uri.AbsoluteUri)));
}
[Test]
diff --git a/src/embed_tests/Modules.cs b/src/embed_tests/Modules.cs
index 6cab4dd07..67fa3d0fc 100644
--- a/src/embed_tests/Modules.cs
+++ b/src/embed_tests/Modules.cs
@@ -28,18 +28,6 @@ public void Dispose()
}
}
- [OneTimeSetUp]
- public void OneTimeSetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void OneTimeTearDown()
- {
- PythonEngine.Shutdown();
- }
-
///
/// Eval a Python expression and obtain its return value.
///
@@ -50,7 +38,7 @@ public void TestEval()
{
ps.Set("a", 1);
var result = ps.Eval("a + 2");
- Assert.AreEqual(3, result);
+ Assert.That(result, Is.EqualTo(3));
}
}
@@ -66,7 +54,7 @@ public void TestExec()
ps.Set("cc", 10); //declare a local variable
ps.Exec("aa = bb + cc + 3");
var result = ps.Get("aa");
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
}
@@ -83,7 +71,7 @@ public void TestCompileExpression()
ps.Set("cc", 10); //declare a local variable
PyObject script = PythonEngine.Compile("bb + cc + 3", "", RunFlagType.Eval);
var result = ps.Execute(script);
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
}
@@ -102,7 +90,7 @@ public void TestCompileStatements()
PyObject script = PythonEngine.Compile("aa = bb + cc + 3", "", RunFlagType.File);
ps.Execute(script);
var result = ps.Get("aa");
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
}
@@ -123,7 +111,7 @@ public void TestScopeFunction()
dynamic func1 = ps.Get("func1");
func1(); //call the function, it can be called any times
var result = ps.Get("bb");
- Assert.AreEqual(100, result);
+ Assert.That(result, Is.EqualTo(100));
ps.Set("bb", 100);
ps.Set("cc", 10);
@@ -134,7 +122,7 @@ public void TestScopeFunction()
dynamic func2 = ps.Get("func2");
func2();
result = ps.Get("bb");
- Assert.AreEqual(20, result);
+ Assert.That(result, Is.EqualTo(20));
}
}
@@ -219,10 +207,10 @@ public void TestCreateModuleWithFilename()
using var modWithName = PyModule.FromString("mod_with_name", "", "some_filename");
- Assert.AreEqual("none", mod.Get("__file__"));
- Assert.AreEqual("none", modWithoutName.Get("__file__"));
- Assert.AreEqual("none", modNullName.Get("__file__"));
- Assert.AreEqual("some_filename", modWithName.Get("__file__"));
+ Assert.That(mod.Get("__file__"), Is.EqualTo("none"));
+ Assert.That(modWithoutName.Get("__file__"), Is.EqualTo("none"));
+ Assert.That(modNullName.Get("__file__"), Is.EqualTo("none"));
+ Assert.That(modWithName.Get("__file__"), Is.EqualTo("some_filename"));
}
///
@@ -235,17 +223,17 @@ public void TestImportModule()
using (Py.GIL())
{
dynamic sys = ps.Import("sys");
- Assert.IsTrue(ps.Contains("sys"));
+ Assert.That(ps.Contains("sys"), Is.True);
ps.Exec("sys.attr1 = 2");
var value1 = ps.Eval("sys.attr1");
var value2 = sys.attr1.As();
- Assert.AreEqual(2, value1);
+ Assert.That(value1, Is.EqualTo(2));
Assert.AreEqual(2, value2);
//import as
ps.Import("sys", "sys1");
- Assert.IsTrue(ps.Contains("sys1"));
+ Assert.That(ps.Contains("sys1"), Is.True);
}
}
@@ -266,10 +254,10 @@ public void TestImportScope()
scope.Import(ps, "ps");
scope.Exec("aa = ps.bb + ps.cc + 3");
var result = scope.Get("aa");
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
- Assert.IsFalse(ps.Contains("aa"));
+ Assert.That(ps.Contains("aa"), Is.False);
}
}
@@ -289,10 +277,10 @@ public void TestImportAllFromScope()
{
scope.Exec("aa = bb + cc + 3");
var result = scope.Get("aa");
- Assert.AreEqual(113, result);
+ Assert.That(result, Is.EqualTo(113));
}
- Assert.IsFalse(ps.Contains("aa"));
+ Assert.That(ps.Contains("aa"), Is.False);
}
}
@@ -345,22 +333,22 @@ public void TestVariables()
{
(ps.Variables() as dynamic)["ee"] = new PyInt(200);
var a0 = ps.Get("ee");
- Assert.AreEqual(200, a0);
+ Assert.That(a0, Is.EqualTo(200));
ps.Exec("locals()['ee'] = 210");
var a1 = ps.Get("ee");
- Assert.AreEqual(210, a1);
+ Assert.That(a1, Is.EqualTo(210));
ps.Exec("globals()['ee'] = 220");
var a2 = ps.Get("ee");
- Assert.AreEqual(220, a2);
+ Assert.That(a2, Is.EqualTo(220));
using (var item = ps.Variables())
{
item["ee"] = new PyInt(230);
}
var a3 = ps.Get("ee");
- Assert.AreEqual(230, a3);
+ Assert.That(a3, Is.EqualTo(230));
}
}
@@ -420,7 +408,7 @@ public void TestThread()
using (Py.GIL())
{
var result = ps.Get("res");
- Assert.AreEqual(101 * th_cnt, result);
+ Assert.That(result, Is.EqualTo(101 * th_cnt));
}
}
finally
@@ -434,7 +422,7 @@ public void TestCreate()
{
using var scope = Py.CreateScope();
- Assert.IsFalse(PyModule.SysModules.HasKey("testmod"));
+ Assert.That(PyModule.SysModules.HasKey("testmod"), Is.False);
PyModule testmod = new PyModule("testmod");
@@ -448,7 +436,7 @@ public void TestCreate()
);
scope.Execute(code);
- Assert.IsTrue(scope.TryGet("x", out dynamic x));
+ Assert.That(scope.TryGet("x", out dynamic x), Is.True);
Assert.AreEqual("True", x.ToString());
}
diff --git a/src/embed_tests/NeedsReinit/StopAndRestartEngine.cs b/src/embed_tests/NeedsReinit/StopAndRestartEngine.cs
new file mode 100644
index 000000000..9ea4f73c5
--- /dev/null
+++ b/src/embed_tests/NeedsReinit/StopAndRestartEngine.cs
@@ -0,0 +1,28 @@
+using Python.Runtime;
+using NUnit.Framework;
+
+namespace Python.EmbeddingTest.NeedsReinit;
+
+public class StopAndRestartEngine
+{
+ bool WasInitialized = false;
+
+ [OneTimeSetUp]
+ public void Setup()
+ {
+ WasInitialized = PythonEngine.IsInitialized;
+ if (WasInitialized)
+ {
+ PythonEngine.Shutdown();
+ }
+ }
+
+ [OneTimeTearDown]
+ public void Teardown()
+ {
+ if (WasInitialized && !PythonEngine.IsInitialized)
+ {
+ PythonEngine.Initialize();
+ }
+ }
+}
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/NeedsReinit/TestDomainReload.cs
similarity index 99%
rename from src/embed_tests/TestDomainReload.cs
rename to src/embed_tests/NeedsReinit/TestDomainReload.cs
index a0f9b63eb..a8d2cd3d8 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/NeedsReinit/TestDomainReload.cs
@@ -15,10 +15,13 @@
// Unfortunately this means no continuous integration testing for this case.
//
#if NETFRAMEWORK
-namespace Python.EmbeddingTest
+namespace Python.EmbeddingTest.NeedsReinit
{
- class TestDomainReload
+ [Category("NeedsReinit")]
+ class TestDomainReload : StopAndRestartEngine
{
+
+
abstract class CrossCaller : MarshalByRefObject
{
public abstract ValueType Execute(ValueType arg);
diff --git a/src/embed_tests/pyinitialize.cs b/src/embed_tests/NeedsReinit/TestPyInitialize.cs
similarity index 95%
rename from src/embed_tests/pyinitialize.cs
rename to src/embed_tests/NeedsReinit/TestPyInitialize.cs
index 25dafb686..1ef4127b8 100644
--- a/src/embed_tests/pyinitialize.cs
+++ b/src/embed_tests/NeedsReinit/TestPyInitialize.cs
@@ -2,9 +2,10 @@
using NUnit.Framework;
using Python.Runtime;
-namespace Python.EmbeddingTest
+namespace Python.EmbeddingTest.NeedsReinit
{
- public class PyInitializeTest
+ [Category("NeedsReinit")]
+ public class TestPyInitialize : StopAndRestartEngine
{
///
/// Tests issue with multiple simple Initialize/Shutdowns.
@@ -42,8 +43,8 @@ public static void LoadSpecificArgs()
{
using var v0 = argv[0];
using var v1 = argv[1];
- Assert.AreEqual(args[0], v0.ToString());
- Assert.AreEqual(args[1], v1.ToString());
+ Assert.That(v0.ToString(), Is.EqualTo(args[0]));
+ Assert.That(v1.ToString(), Is.EqualTo(args[1]));
}
}
}
diff --git a/src/embed_tests/NeedsReinit/TestPythonEngineProperties.cs b/src/embed_tests/NeedsReinit/TestPythonEngineProperties.cs
new file mode 100644
index 000000000..8eb9e975d
--- /dev/null
+++ b/src/embed_tests/NeedsReinit/TestPythonEngineProperties.cs
@@ -0,0 +1,133 @@
+using System;
+using NUnit.Framework;
+using Python.Runtime;
+
+namespace Python.EmbeddingTest.NeedsReinit
+{
+ [Category("NeedsReinit")]
+ public class TestPythonEngineProperties : StopAndRestartEngine
+ {
+ [Test]
+ public void SetPythonHome()
+ {
+ PythonEngine.Initialize();
+ var pythonHomeBackup = PythonEngine.PythonHome;
+ PythonEngine.Shutdown();
+
+ if (pythonHomeBackup == "")
+ Assert.Inconclusive("Can't reset PythonHome to empty string, skipping");
+
+ var pythonHome = "/dummypath/";
+
+ PythonEngine.PythonHome = pythonHome;
+ PythonEngine.Initialize();
+
+ Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
+ PythonEngine.Shutdown();
+
+ // Restoring valid pythonhome.
+ PythonEngine.PythonHome = pythonHomeBackup;
+ }
+
+ [Test]
+ public void SetPythonHomeTwice()
+ {
+ PythonEngine.Initialize();
+ var pythonHomeBackup = PythonEngine.PythonHome;
+ PythonEngine.Shutdown();
+
+ if (pythonHomeBackup == "")
+ Assert.Inconclusive("Can't reset PythonHome to empty string, skipping");
+
+ var pythonHome = "/dummypath/";
+
+ PythonEngine.PythonHome = "/dummypath2/";
+ PythonEngine.PythonHome = pythonHome;
+ PythonEngine.Initialize();
+
+ Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
+ PythonEngine.Shutdown();
+
+ PythonEngine.PythonHome = pythonHomeBackup;
+ }
+
+ [Test]
+ [Ignore("Currently buggy in Python")]
+ public void SetPythonHomeEmptyString()
+ {
+ PythonEngine.Initialize();
+
+ var backup = PythonEngine.PythonHome;
+ if (backup == "")
+ {
+ PythonEngine.Shutdown();
+ Assert.Inconclusive("Can't reset PythonHome to empty string, skipping");
+ }
+ PythonEngine.PythonHome = "";
+
+ Assert.AreEqual("", PythonEngine.PythonHome);
+
+ PythonEngine.PythonHome = backup;
+ PythonEngine.Shutdown();
+ }
+
+ [Test]
+ public void SetProgramName()
+ {
+ if (PythonEngine.IsInitialized)
+ {
+ PythonEngine.Shutdown();
+ }
+
+ var programNameBackup = PythonEngine.ProgramName;
+
+ var programName = "FooBar";
+
+ PythonEngine.ProgramName = programName;
+ PythonEngine.Initialize();
+
+ Assert.AreEqual(programName, PythonEngine.ProgramName);
+ PythonEngine.Shutdown();
+
+ PythonEngine.ProgramName = programNameBackup;
+ }
+
+ [Test]
+ public void SetPythonPath()
+ {
+ PythonEngine.Initialize();
+
+ const string moduleName = "pytest";
+ bool importShouldSucceed;
+ try
+ {
+ Py.Import(moduleName);
+ importShouldSucceed = true;
+ }
+ catch
+ {
+ importShouldSucceed = false;
+ }
+
+ string[] paths = Py.Import("sys").GetAttr("path").As();
+ string path = string.Join(System.IO.Path.PathSeparator.ToString(), paths);
+
+ // path should not be set to PythonEngine.PythonPath here.
+ // PythonEngine.PythonPath gets the default module search path, not the full search path.
+ // The list sys.path is initialized with this value on interpreter startup;
+ // it can be (and usually is) modified later to change the search path for loading modules.
+ // See https://docs.python.org/3/c-api/init.html#c.Py_GetPath
+ // After PythonPath is set, then PythonEngine.PythonPath will correctly return the full search path.
+
+ PythonEngine.Shutdown();
+
+ PythonEngine.PythonPath = path;
+ PythonEngine.Initialize();
+
+ Assert.AreEqual(path, PythonEngine.PythonPath);
+ if (importShouldSucceed) Py.Import(moduleName);
+
+ PythonEngine.Shutdown();
+ }
+ }
+}
diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/NeedsReinit/TestRuntime.cs
similarity index 94%
rename from src/embed_tests/TestRuntime.cs
rename to src/embed_tests/NeedsReinit/TestRuntime.cs
index 77696fd96..193bf57d3 100644
--- a/src/embed_tests/TestRuntime.cs
+++ b/src/embed_tests/NeedsReinit/TestRuntime.cs
@@ -3,20 +3,11 @@
using NUnit.Framework;
using Python.Runtime;
-namespace Python.EmbeddingTest
+namespace Python.EmbeddingTest.NeedsReinit
{
- public class TestRuntime
+ [Ignore("Tests for low-level Runtime functions, crashing currently")]
+ public class TestRuntime : StopAndRestartEngine
{
- [OneTimeSetUp]
- public void SetUp()
- {
- // We needs to ensure that no any engines are running.
- if (PythonEngine.IsInitialized)
- {
- PythonEngine.Shutdown();
- }
- }
-
[Test]
public static void Py_IsInitializedValue()
{
diff --git a/src/embed_tests/NumPyTests.cs b/src/embed_tests/NumPyTests.cs
index e102ddb99..6f4a85716 100644
--- a/src/embed_tests/NumPyTests.cs
+++ b/src/embed_tests/NumPyTests.cs
@@ -13,14 +13,13 @@ public class NumPyTests
[OneTimeSetUp]
public void SetUp()
{
- PythonEngine.Initialize();
TupleCodec.Register();
}
[OneTimeTearDown]
public void Dispose()
{
- PythonEngine.Shutdown();
+ PyObjectConversions.Reset();
}
[Test]
@@ -32,7 +31,7 @@ public void TestReadme()
StringAssert.StartsWith("-0.95892", sin(5).ToString());
double c = (double)(np.cos(5) + sin(5));
- Assert.AreEqual(-0.675262, c, 0.01);
+ Assert.That(c, Is.EqualTo(-0.675262).Within(0.01));
dynamic a = np.array(new List { 1, 2, 3 });
Assert.AreEqual("float64", a.dtype.ToString());
diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj
index 4993994d3..15258fc83 100644
--- a/src/embed_tests/Python.EmbeddingTest.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.csproj
@@ -1,7 +1,7 @@
- net472;net6.0
+ net472;net10.0
..\pythonnet.snk
true
@@ -14,19 +14,28 @@
+
+
+
+
+
$(DefineConstants);$(ConfiguredConstants)
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
- 1.0.0
+ 1.*
all
runtime; build; native; contentfiles; analyzers
diff --git a/src/embed_tests/References.cs b/src/embed_tests/References.cs
index c416c5ebe..af9e74336 100644
--- a/src/embed_tests/References.cs
+++ b/src/embed_tests/References.cs
@@ -5,18 +5,6 @@ namespace Python.EmbeddingTest
public class References
{
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void MoveToPyObject_SetsNull()
{
@@ -24,10 +12,10 @@ public void MoveToPyObject_SetsNull()
NewReference reference = Runtime.PyDict_Items(dict.Reference);
try
{
- Assert.IsFalse(reference.IsNull());
+ Assert.That(reference.IsNull(), Is.False);
using (reference.MoveToPyObject())
- Assert.IsTrue(reference.IsNull());
+ Assert.That(reference.IsNull(), Is.True);
}
finally
{
diff --git a/src/embed_tests/StateSerialization/MethodSerialization.cs b/src/embed_tests/StateSerialization/MethodSerialization.cs
index 80b7a08ee..d565c1e7a 100644
--- a/src/embed_tests/StateSerialization/MethodSerialization.cs
+++ b/src/embed_tests/StateSerialization/MethodSerialization.cs
@@ -20,7 +20,7 @@ public void GenericRoundtrip()
}
[Test]
- public void ConstrctorRoundtrip()
+ public void ConstructorRoundtrip()
{
var ctor = typeof(MethodTestHost).GetConstructor(new[] { typeof(int) });
var maybeConstructor = new MaybeMethodBase(ctor);
@@ -33,6 +33,10 @@ static T SerializationRoundtrip(T item)
{
using var buf = new MemoryStream();
var formatter = RuntimeData.CreateFormatter();
+ if (typeof(NoopFormatter).IsAssignableFrom(formatter.GetType()))
+ {
+ Assert.Inconclusive("NoopFormatter in use, cannot perform serialization test.");
+ }
formatter.Serialize(buf, item);
buf.Position = 0;
return (T)formatter.Deserialize(buf);
diff --git a/src/embed_tests/TestCallbacks.cs b/src/embed_tests/TestCallbacks.cs
index 88b84d0c3..7e9583364 100644
--- a/src/embed_tests/TestCallbacks.cs
+++ b/src/embed_tests/TestCallbacks.cs
@@ -5,16 +5,6 @@
namespace Python.EmbeddingTest {
public class TestCallbacks {
- [OneTimeSetUp]
- public void SetUp() {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose() {
- PythonEngine.Shutdown();
- }
-
[Test]
public void TestNoOverloadException() {
int passed = 0;
@@ -23,7 +13,7 @@ public void TestNoOverloadException() {
using dynamic callWith42 = PythonEngine.Eval("lambda f: f([42])");
using var pyFunc = aFunctionThatCallsIntoPython.ToPython();
var error = Assert.Throws(() => callWith42(pyFunc));
- Assert.AreEqual("TypeError", error.Type.Name);
+ Assert.That(error.Type.Name, Is.EqualTo("TypeError"));
string expectedArgTypes = "()";
StringAssert.EndsWith(expectedArgTypes, error.Message);
error.Traceback.Dispose();
diff --git a/src/embed_tests/TestConverter.cs b/src/embed_tests/TestConverter.cs
index a59b9c97b..3feced8d0 100644
--- a/src/embed_tests/TestConverter.cs
+++ b/src/embed_tests/TestConverter.cs
@@ -23,18 +23,6 @@ public class TestConverter
typeof(ulong)
};
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
[Test]
public void TestConvertSingleToManaged(
[Values(float.PositiveInfinity, float.NegativeInfinity, float.MinValue, float.MaxValue, float.NaN,
@@ -45,8 +33,8 @@ public void TestConvertSingleToManaged(
object convertedValue;
var converted = Converter.ToManaged(pyFloat, typeof(float), out convertedValue, false);
- Assert.IsTrue(converted);
- Assert.IsTrue(((float) convertedValue).Equals(testValue));
+ Assert.That(converted, Is.True);
+ Assert.That(((float)convertedValue).Equals(testValue), Is.True);
}
[Test]
@@ -59,8 +47,8 @@ public void TestConvertDoubleToManaged(
object convertedValue;
var converted = Converter.ToManaged(pyFloat, typeof(double), out convertedValue, false);
- Assert.IsTrue(converted);
- Assert.IsTrue(((double) convertedValue).Equals(testValue));
+ Assert.That(converted, Is.True);
+ Assert.That(((double)convertedValue).Equals(testValue), Is.True);
}
[Test]
@@ -79,10 +67,10 @@ public void CovertTypeError()
try
{
bool res = Converter.ToManaged(s, type, out value, true);
- Assert.IsFalse(res);
+ Assert.That(res, Is.False);
var bo = Exceptions.ExceptionMatches(Exceptions.TypeError);
- Assert.IsTrue(Exceptions.ExceptionMatches(Exceptions.TypeError)
- || Exceptions.ExceptionMatches(Exceptions.ValueError));
+ Assert.That(Exceptions.ExceptionMatches(Exceptions.TypeError)
+ || Exceptions.ExceptionMatches(Exceptions.ValueError), Is.True);
}
finally
{
@@ -104,8 +92,8 @@ public void ConvertOverflow()
foreach (var type in _numTypes)
{
bool res = Converter.ToManaged(largeNum.BorrowOrThrow(), type, out value, true);
- Assert.IsFalse(res);
- Assert.IsTrue(Exceptions.ExceptionMatches(Exceptions.OverflowError));
+ Assert.That(res, Is.False);
+ Assert.That(Exceptions.ExceptionMatches(Exceptions.OverflowError), Is.True);
Exceptions.Clear();
}
}
@@ -129,7 +117,7 @@ public void ToNullable()
const int Const = 42;
var i = new PyInt(Const);
var ni = i.As();
- Assert.AreEqual(Const, ni);
+ Assert.That(ni, Is.EqualTo(Const));
}
[Test]
@@ -138,9 +126,9 @@ public void BigIntExplicit()
BigInteger val = 42;
var i = new PyInt(val);
var ni = i.As();
- Assert.AreEqual(val, ni);
+ Assert.That(ni, Is.EqualTo(val));
var nullable = i.As();
- Assert.AreEqual(val, nullable);
+ Assert.That(nullable, Is.EqualTo(val));
}
[Test]
@@ -148,7 +136,7 @@ public void PyIntImplicit()
{
var i = new PyInt(1);
var ni = (PyObject)i.As
diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs
index 0b28c3a35..13855adef 100644
--- a/src/runtime/PythonEngine.cs
+++ b/src/runtime/PythonEngine.cs
@@ -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
diff --git a/src/runtime/Runtime.Delegates.cs b/src/runtime/Runtime.Delegates.cs
index 262dc1e19..dc4a4b0a9 100644
--- a/src/runtime/Runtime.Delegates.cs
+++ b/src/runtime/Runtime.Delegates.cs
@@ -308,7 +308,8 @@ static Delegates()
{
throw new BadPythonDllException(
"Runtime.PythonDLL was not set or does not point to a supported Python runtime DLL." +
- " See https://github.com/pythonnet/pythonnet#embedding-python-in-net",
+ " See https://github.com/pythonnet/pythonnet#embedding-python-in-net." +
+ $" Value of PythonDLL: {PythonDLL ?? "null"}",
e);
}
}
diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs
index c8f022860..399608733 100644
--- a/src/runtime/Runtime.cs
+++ b/src/runtime/Runtime.cs
@@ -5,6 +5,7 @@
using System.Text;
using System.Threading;
using System.Collections.Generic;
+using System.IO;
using Python.Runtime.Native;
using System.Linq;
using static System.FormattableString;
@@ -18,6 +19,8 @@ namespace Python.Runtime
///
public unsafe partial class Runtime
{
+ internal static PythonEnvironment PythonEnvironment = PythonEnvironment.FromEnv();
+
public static string? PythonDLL
{
get => _PythonDll;
@@ -25,33 +28,11 @@ public static string? PythonDLL
{
if (_isInitialized)
throw new InvalidOperationException("This property must be set before runtime is initialized");
- _PythonDll = value;
+ PythonEnvironment.LibPython = value;
}
}
- static string? _PythonDll = GetDefaultDllName();
- private static string? GetDefaultDllName()
- {
- string dll = Environment.GetEnvironmentVariable("PYTHONNET_PYDLL");
- if (dll is not null) return dll;
-
- string verString = Environment.GetEnvironmentVariable("PYTHONNET_PYVER");
- if (!Version.TryParse(verString, out var version)) return null;
-
- return GetDefaultDllName(version);
- }
-
- private static string GetDefaultDllName(Version version)
- {
- string prefix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "" : "lib";
- string suffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
- ? Invariant($"{version.Major}{version.Minor}")
- : Invariant($"{version.Major}.{version.Minor}");
- string ext = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".dll"
- : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? ".dylib"
- : ".so";
- return prefix + "python" + suffix + ext;
- }
+ static string? _PythonDll => PythonEnvironment.LibPython;
private static bool _isInitialized = false;
internal static bool IsInitialized => _isInitialized;
@@ -96,6 +77,18 @@ internal static int GetRun()
return runNumber;
}
+ static void EnsureProgramName()
+ {
+ if (!string.IsNullOrEmpty(PythonEngine.ProgramName))
+ return;
+
+ if (PythonEnvironment.IsValid)
+ {
+ PythonEngine.ProgramName = PythonEnvironment.ProgramName!;
+ return;
+ }
+ }
+
internal static bool HostedInPython;
internal static bool ProcessIsTerminating;
@@ -117,6 +110,8 @@ internal static void Initialize(bool initSigs = false)
);
if (!interpreterAlreadyInitialized)
{
+ EnsureProgramName();
+
Py_InitializeEx(initSigs ? 1 : 0);
NewRun();
diff --git a/src/runtime/StateSerialization/RuntimeData.cs b/src/runtime/StateSerialization/RuntimeData.cs
index 8eda9ce0b..61e377aa4 100644
--- a/src/runtime/StateSerialization/RuntimeData.cs
+++ b/src/runtime/StateSerialization/RuntimeData.cs
@@ -20,7 +20,9 @@ public static class RuntimeData
{
try
{
- return new BinaryFormatter();
+ var res = new BinaryFormatter();
+ res.Serialize(new MemoryStream(), 1); // test if BinaryFormatter is usable
+ return res;
}
catch
{
diff --git a/src/runtime/TypeManager.cs b/src/runtime/TypeManager.cs
index 559d5148e..dbff1fbd4 100644
--- a/src/runtime/TypeManager.cs
+++ b/src/runtime/TypeManager.cs
@@ -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;
diff --git a/src/runtime/Types/ClassBase.cs b/src/runtime/Types/ClassBase.cs
index 2d6ce8a47..3fcb7ca4f 100644
--- a/src/runtime/Types/ClassBase.cs
+++ b/src/runtime/Types/ClassBase.cs
@@ -374,6 +374,8 @@ public static int tp_clear(BorrowedReference ob)
return 0;
}
+ static readonly HashSet ClearVisited = new();
+
internal static unsafe int BaseUnmanagedClear(BorrowedReference ob)
{
var type = Runtime.PyObject_TYPE(ob);
@@ -385,21 +387,20 @@ internal static unsafe int BaseUnmanagedClear(BorrowedReference ob)
}
var clear = (delegate* unmanaged[Cdecl])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 OnSave(BorrowedReference ob)
diff --git a/src/runtime/Types/ManagedTypes.cd b/src/runtime/Types/ManagedTypes.cd
index 9a3e3de16..e6759265f 100644
--- a/src/runtime/Types/ManagedTypes.cd
+++ b/src/runtime/Types/ManagedTypes.cd
@@ -1,4 +1,4 @@
-
+
diff --git a/src/runtime/Types/MetaType.cs b/src/runtime/Types/MetaType.cs
index 57fcaa232..ecbc9cac5 100644
--- a/src/runtime/Types/MetaType.cs
+++ b/src/runtime/Types/MetaType.cs
@@ -21,6 +21,7 @@ internal sealed class MetaType : ManagedType
// set in Initialize
private static PyType PyCLRMetaType;
private static SlotsHolder _metaSlotsHodler;
+ private static int TypeDictOffset = -1;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
internal static readonly string[] CustomMethods = new string[]
@@ -35,6 +36,25 @@ internal sealed class MetaType : ManagedType
public static PyType Initialize()
{
PyCLRMetaType = TypeManager.CreateMetaType(typeof(MetaType), out _metaSlotsHodler);
+
+ // Retrieve the offset of the type's dictionary from PyType_Type for
+ // use in the tp_setattro implementation.
+ using (NewReference dictOffset = Runtime.PyObject_GetAttr(Runtime.PyTypeType, PyIdentifier.__dictoffset__))
+ {
+ if (dictOffset.IsNull())
+ {
+ throw new InvalidOperationException("Could not get __dictoffset__ from PyType_Type");
+ }
+
+ nint dictOffsetVal = Runtime.PyLong_AsSignedSize_t(dictOffset.Borrow());
+ if (dictOffsetVal <= 0)
+ {
+ throw new InvalidOperationException("Could not get __dictoffset__ from PyType_Type");
+ }
+
+ TypeDictOffset = checked((int)dictOffsetVal);
+ }
+
return PyCLRMetaType;
}
@@ -44,6 +64,7 @@ public static void Release()
{
_metaSlotsHodler.ResetSlots();
}
+ TypeDictOffset = -1;
PyCLRMetaType.Dispose();
}
@@ -287,7 +308,28 @@ public static int tp_setattro(BorrowedReference tp, BorrowedReference name, Borr
}
}
- int res = Runtime.PyObject_GenericSetAttr(tp, name, value);
+ // Access the type's dictionary directly
+ //
+ // We can not use the PyObject_GenericSetAttr because since Python
+ // 3.14 as https://github.com/python/cpython/pull/118454 intrdoduced
+ // an assertion to prevent it from being called from metatypes.
+ //
+ // The direct dictionary access is equivalent to what Cython does
+ // to work around the same issue: https://github.com/cython/cython/pull/6325
+ BorrowedReference typeDict = new(Util.ReadIntPtr(tp, TypeDictOffset));
+ int res;
+ if (value.IsNull)
+ {
+ res = Runtime.PyDict_DelItem(typeDict, name);
+ if (res != 0)
+ {
+ Exceptions.SetError(Exceptions.AttributeError, "attribute not found");
+ }
+ }
+ else
+ {
+ res = Runtime.PyDict_SetItem(typeDict, name, value);
+ }
Runtime.PyType_Modified(tp);
return res;
diff --git a/src/runtime/Util/PythonEnvironment.cs b/src/runtime/Util/PythonEnvironment.cs
new file mode 100644
index 000000000..b1ebc7fa5
--- /dev/null
+++ b/src/runtime/Util/PythonEnvironment.cs
@@ -0,0 +1,188 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using static System.FormattableString;
+
+namespace Python.Runtime;
+
+
+internal class PythonEnvironment
+{
+ readonly static string PYDLL_ENV_VAR = "PYTHONNET_PYDLL";
+ readonly static string PYEXE_ENV_VAR = "PYTHONNET_PYEXE";
+ readonly static string PYNET_VENV_ENV_VAR = "PYTHONNET_VENV";
+ readonly static string VENV_ENV_VAR = "VIRTUAL_ENV";
+
+ public string? VenvPath { get; private set; }
+ public string? Home { get; private set; }
+ public Version? Version { get; private set; }
+ public string? ProgramName { get; set; }
+ public string? LibPython { get; set; }
+
+ public bool IsValid =>
+ !string.IsNullOrEmpty(ProgramName) && !string.IsNullOrEmpty(LibPython);
+
+
+ // TODO: Move the lib-guessing step to separate function, use together with
+ // PYTHONNET_PYEXE or a path lookup as last resort
+
+ // Initialize PythonEnvironment instance from environment variables.
+ //
+ // If PYTHONNET_PYEXE and PYTHONNET_PYDLL are set, these always have precedence.
+ // If PYTHONNET_VENV or VIRTUAL_ENV is set, we interpret the environment as a venv
+ // and set the ProgramName/LibPython accordingly. PYTHONNET_VENV takes precedence.
+ public static PythonEnvironment FromEnv()
+ {
+ var pydll = Environment.GetEnvironmentVariable(PYDLL_ENV_VAR);
+ var pydllSet = !string.IsNullOrEmpty(pydll);
+ var pyexe = Environment.GetEnvironmentVariable(PYEXE_ENV_VAR);
+ var pyexeSet = !string.IsNullOrEmpty(pyexe);
+ var pynetVenv = Environment.GetEnvironmentVariable(PYNET_VENV_ENV_VAR);
+ var pynetVenvSet = !string.IsNullOrEmpty(pynetVenv);
+ var venv = Environment.GetEnvironmentVariable(VENV_ENV_VAR);
+ var venvSet = !string.IsNullOrEmpty(venv);
+
+ PythonEnvironment? res = new();
+
+ if (pynetVenvSet)
+ res = FromVenv(pynetVenv) ?? res;
+ else if (venvSet)
+ res = FromVenv(venv) ?? res;
+
+ if (pyexeSet)
+ res.ProgramName = pyexe;
+
+ if (pydllSet)
+ res.LibPython = pydll;
+
+ return res;
+ }
+
+ public static PythonEnvironment? FromVenv(string path)
+ {
+ var env = new PythonEnvironment
+ {
+ VenvPath = path
+ };
+
+ string venvCfg = Path.Combine(path, "pyvenv.cfg");
+
+ if (!File.Exists(venvCfg))
+ return null;
+
+ var settings = TryParse(venvCfg);
+
+ if (!settings.ContainsKey("home"))
+ return null;
+
+ env.Home = settings["home"];
+ var pname = ProgramNameFromPath(path);
+ if (File.Exists(pname))
+ env.ProgramName = pname;
+
+ if (settings.TryGetValue("version", out string versionStr))
+ {
+ _ = Version.TryParse(versionStr, out Version versionObj);
+ env.Version = versionObj;
+ }
+ else if (settings.TryGetValue("version_info", out versionStr))
+ {
+ _ = Version.TryParse(versionStr, out Version versionObj);
+ env.Version = versionObj;
+ }
+
+ env.LibPython = FindLibPython(env.Home, env.Version);
+
+ return env;
+ }
+
+ private static Dictionary TryParse(string venvCfg)
+ {
+ var settings = new Dictionary();
+
+ string[] lines = File.ReadAllLines(venvCfg);
+
+ // The actually used format is really primitive: " = "
+ foreach (string line in lines)
+ {
+ var split = line.Split(new[] { '=' }, 2);
+
+ if (split.Length != 2)
+ continue;
+
+ settings[split[0].Trim()] = split[1].Trim();
+ }
+
+ return settings;
+ }
+
+ private static string? FindLibPython(string home, Version? maybeVersion)
+ {
+ // TODO: Check whether there is a .dll/.so/.dylib next to the executable
+
+ if (maybeVersion is Version version)
+ {
+ return FindLibPythonInHome(home, version);
+ }
+
+ return null;
+ }
+
+ private static string? FindLibPythonInHome(string home, Version version)
+ {
+ var libPythonName = GetDefaultDllName(version);
+
+ List pathsToCheck = new();
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ var arch = RuntimeInformation.ProcessArchitecture;
+ if (arch == Architecture.X64 || arch == Architecture.Arm64)
+ {
+ // multilib systems
+ pathsToCheck.Add("../lib64");
+ }
+ pathsToCheck.Add("../lib");
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ pathsToCheck.Add(".");
+ }
+ else
+ {
+ pathsToCheck.Add("../lib");
+ }
+
+ return pathsToCheck
+ .Select(path => Path.Combine(home, path, libPythonName))
+ .FirstOrDefault(File.Exists);
+ }
+
+ private static string ProgramNameFromPath(string path)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return Path.Combine(path, "Scripts", "python.exe");
+ }
+ else
+ {
+ return Path.Combine(path, "bin", "python");
+ }
+ }
+
+ internal static string GetDefaultDllName(Version version)
+ {
+ string prefix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "" : "lib";
+
+ string suffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
+ ? Invariant($"{version.Major}{version.Minor}")
+ : Invariant($"{version.Major}.{version.Minor}");
+
+ string ext = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".dll"
+ : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? ".dylib"
+ : ".so";
+
+ return prefix + "python" + suffix + ext;
+ }
+}
diff --git a/src/testing/Python.Test.csproj b/src/testing/Python.Test.csproj
index 3adc5c0c6..c2455b1a5 100644
--- a/src/testing/Python.Test.csproj
+++ b/src/testing/Python.Test.csproj
@@ -1,6 +1,6 @@
- netstandard2.0;net6.0
+ netstandard2.0;net10.0
true
true
..\pythonnet.snk
diff --git a/tests/domain_tests/App.config b/tests/domain_tests/App.config
index 56efbc7b5..20939707c 100644
--- a/tests/domain_tests/App.config
+++ b/tests/domain_tests/App.config
@@ -1,4 +1,4 @@
-
+
diff --git a/tests/domain_tests/Python.DomainReloadTests.csproj b/tests/domain_tests/Python.DomainReloadTests.csproj
index 9cb61c6f4..a7d6d4f6b 100644
--- a/tests/domain_tests/Python.DomainReloadTests.csproj
+++ b/tests/domain_tests/Python.DomainReloadTests.csproj
@@ -14,13 +14,17 @@
-
+
-
+
+ 1.*
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/tests/test_method.py b/tests/test_method.py
index c70200c7e..da37afd88 100644
--- a/tests/test_method.py
+++ b/tests/test_method.py
@@ -967,8 +967,9 @@ def test_getting_generic_method_binding_does_not_leak_memory():
bytesAllocatedPerIteration = pow(2, 20) # 1MB
bytesLeakedPerIteration = processBytesDelta / iterations
- # Allow 50% threshold - this shows the original issue is fixed, which leaks the full allocated bytes per iteration
- failThresholdBytesLeakedPerIteration = bytesAllocatedPerIteration / 2
+ # Allow 75% threshold - this shows the original issue is fixed, which leaks the full allocated bytes per iteration
+ # Increased from 50% to ensure that it works on Windows with Python >3.13
+ failThresholdBytesLeakedPerIteration = bytesAllocatedPerIteration * 0.75
assert bytesLeakedPerIteration < failThresholdBytesLeakedPerIteration
diff --git a/tests/test_mp_length.py b/tests/test_mp_length.py
index e86fff288..8b6e56b7c 100644
--- a/tests/test_mp_length.py
+++ b/tests/test_mp_length.py
@@ -1,63 +1,63 @@
-# -*- coding: utf-8 -*-
-
-"""Test __len__ for .NET classes implementing ICollection/ICollection."""
-
-import System
-import pytest
-from Python.Test import MpLengthCollectionTest, MpLengthExplicitCollectionTest, MpLengthGenericCollectionTest, MpLengthExplicitGenericCollectionTest
-
-def test_simple___len__():
- """Test __len__ for simple ICollection implementers"""
- import System
- import System.Collections.Generic
- l = System.Collections.Generic.List[int]()
- assert len(l) == 0
- l.Add(5)
- l.Add(6)
- assert len(l) == 2
-
- d = System.Collections.Generic.Dictionary[int, int]()
- assert len(d) == 0
- d.Add(4, 5)
- assert len(d) == 1
-
- a = System.Array[int]([0,1,2,3])
- assert len(a) == 4
-
-def test_custom_collection___len__():
- """Test __len__ for custom collection class"""
- s = MpLengthCollectionTest()
- assert len(s) == 3
-
-def test_custom_collection_explicit___len__():
- """Test __len__ for custom collection class that explicitly implements ICollection"""
- s = MpLengthExplicitCollectionTest()
- assert len(s) == 2
-
-def test_custom_generic_collection___len__():
- """Test __len__ for custom generic collection class"""
- s = MpLengthGenericCollectionTest[int]()
- s.Add(1)
- s.Add(2)
- assert len(s) == 2
-
-def test_custom_generic_collection_explicit___len__():
- """Test __len__ for custom generic collection that explicity implements ICollection"""
- s = MpLengthExplicitGenericCollectionTest[int]()
- s.Add(1)
- s.Add(10)
- assert len(s) == 2
-
-def test_len_through_interface_generic():
- """Test __len__ for ICollection"""
- import System.Collections.Generic
- l = System.Collections.Generic.List[int]()
- coll = System.Collections.Generic.ICollection[int](l)
- assert len(coll) == 0
-
-def test_len_through_interface():
- """Test __len__ for ICollection"""
- import System.Collections
- l = System.Collections.ArrayList()
- coll = System.Collections.ICollection(l)
- assert len(coll) == 0
+# -*- coding: utf-8 -*-
+
+"""Test __len__ for .NET classes implementing ICollection/ICollection."""
+
+import System
+import pytest
+from Python.Test import MpLengthCollectionTest, MpLengthExplicitCollectionTest, MpLengthGenericCollectionTest, MpLengthExplicitGenericCollectionTest
+
+def test_simple___len__():
+ """Test __len__ for simple ICollection implementers"""
+ import System
+ import System.Collections.Generic
+ l = System.Collections.Generic.List[int]()
+ assert len(l) == 0
+ l.Add(5)
+ l.Add(6)
+ assert len(l) == 2
+
+ d = System.Collections.Generic.Dictionary[int, int]()
+ assert len(d) == 0
+ d.Add(4, 5)
+ assert len(d) == 1
+
+ a = System.Array[int]([0,1,2,3])
+ assert len(a) == 4
+
+def test_custom_collection___len__():
+ """Test __len__ for custom collection class"""
+ s = MpLengthCollectionTest()
+ assert len(s) == 3
+
+def test_custom_collection_explicit___len__():
+ """Test __len__ for custom collection class that explicitly implements ICollection"""
+ s = MpLengthExplicitCollectionTest()
+ assert len(s) == 2
+
+def test_custom_generic_collection___len__():
+ """Test __len__ for custom generic collection class"""
+ s = MpLengthGenericCollectionTest[int]()
+ s.Add(1)
+ s.Add(2)
+ assert len(s) == 2
+
+def test_custom_generic_collection_explicit___len__():
+ """Test __len__ for custom generic collection that explicity implements ICollection"""
+ s = MpLengthExplicitGenericCollectionTest[int]()
+ s.Add(1)
+ s.Add(10)
+ assert len(s) == 2
+
+def test_len_through_interface_generic():
+ """Test __len__ for ICollection"""
+ import System.Collections.Generic
+ l = System.Collections.Generic.List[int]()
+ coll = System.Collections.Generic.ICollection[int](l)
+ assert len(coll) == 0
+
+def test_len_through_interface():
+ """Test __len__ for ICollection"""
+ import System.Collections
+ l = System.Collections.ArrayList()
+ coll = System.Collections.ICollection(l)
+ assert len(coll) == 0
diff --git a/tools/geninterop/geninterop.py b/tools/geninterop/geninterop.py
index 6d80bcfa6..89186737f 100755
--- a/tools/geninterop/geninterop.py
+++ b/tools/geninterop/geninterop.py
@@ -1,6 +1,7 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
+#!/usr/bin/env uv run
+# /// script
+# dependencies = ["pycparser"]
+# ///
"""
TypeOffset is a C# class that mirrors the in-memory layout of heap
allocated Python objects.
diff --git a/uv.lock b/uv.lock
new file mode 100644
index 000000000..b5230d788
--- /dev/null
+++ b/uv.lock
@@ -0,0 +1,460 @@
+version = 1
+revision = 3
+requires-python = ">=3.10, <3.15"
+resolution-markers = [
+ "python_full_version >= '3.11'",
+ "python_full_version < '3.11'",
+]
+
+[[package]]
+name = "cffi"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" },
+ { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" },
+ { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" },
+ { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" },
+ { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" },
+ { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" },
+ { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" },
+ { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" },
+ { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
+ { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
+ { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
+ { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
+ { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
+ { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
+ { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
+ { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
+ { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
+ { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
+ { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
+ { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
+ { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
+ { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
+]
+
+[[package]]
+name = "clr-loader"
+version = "0.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fe/56/0fb4f734a5b2574b9b75157eabef64e5e2ceaf44b759306034e8b1452e62/clr_loader-0.3.0.tar.gz", hash = "sha256:b880e0821cdc18f9bf9f05e5130e966cc78fa75edc7432baf4fa4711e8412b05", size = 84710, upload-time = "2026-03-03T00:41:51.314Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a2/07/6c965da95ef2b7410f1314cdfe462efdf9722bfd7fbfe6945564b8b0467a/clr_loader-0.3.0-py3-none-any.whl", hash = "sha256:d918467eb1077d23b48b0b7e9b6379e8fbf20b573832839a41cec1e06dad2beb", size = 57431, upload-time = "2026-03-03T00:41:06.554Z" },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
+]
+
+[[package]]
+name = "find-libpython"
+version = "0.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/70/60/951b7ca316ab3ec928ed788de5fcb30b4a0292704e50b872c8edf24c11fe/find_libpython-0.5.1.tar.gz", hash = "sha256:12a0fb39ff8dcc64ad0fd554b1bd142ea4a8c4c18e5da6043a547ce7b25559fe", size = 9402, upload-time = "2026-02-11T03:18:04.844Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/34/1f/1d6079f4f0540aaa368aa20d89d98eda42f081c397a822c547340e32d1e3/find_libpython-0.5.1-py3-none-any.whl", hash = "sha256:723a8cfe6fed255a1f58b53c62ed556fb340ec0d456e9863ebc01a5cc047607d", size = 9201, upload-time = "2026-02-11T03:18:03.263Z" },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.2.6"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version < '3.11'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
+ { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
+ { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
+ { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
+ { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
+ { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
+ { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
+ { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
+ { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
+ { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
+ { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
+ { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" },
+ { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" },
+ { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" },
+ { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" },
+ { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" },
+ { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" },
+ { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" },
+ { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" },
+ { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" },
+ { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" },
+ { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
+ { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
+ { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
+ { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
+]
+
+[[package]]
+name = "numpy"
+version = "2.4.2"
+source = { registry = "https://pypi.org/simple" }
+resolution-markers = [
+ "python_full_version >= '3.11'",
+]
+sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d3/44/71852273146957899753e69986246d6a176061ea183407e95418c2aa4d9a/numpy-2.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825", size = 16955478, upload-time = "2026-01-31T23:10:25.623Z" },
+ { url = "https://files.pythonhosted.org/packages/74/41/5d17d4058bd0cd96bcbd4d9ff0fb2e21f52702aab9a72e4a594efa18692f/numpy-2.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1", size = 14965467, upload-time = "2026-01-31T23:10:28.186Z" },
+ { url = "https://files.pythonhosted.org/packages/49/48/fb1ce8136c19452ed15f033f8aee91d5defe515094e330ce368a0647846f/numpy-2.4.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7", size = 5475172, upload-time = "2026-01-31T23:10:30.848Z" },
+ { url = "https://files.pythonhosted.org/packages/40/a9/3feb49f17bbd1300dd2570432961f5c8a4ffeff1db6f02c7273bd020a4c9/numpy-2.4.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73", size = 6805145, upload-time = "2026-01-31T23:10:32.352Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/39/fdf35cbd6d6e2fcad42fcf85ac04a85a0d0fbfbf34b30721c98d602fd70a/numpy-2.4.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1", size = 15966084, upload-time = "2026-01-31T23:10:34.502Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/46/6fa4ea94f1ddf969b2ee941290cca6f1bfac92b53c76ae5f44afe17ceb69/numpy-2.4.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32", size = 16899477, upload-time = "2026-01-31T23:10:37.075Z" },
+ { url = "https://files.pythonhosted.org/packages/09/a1/2a424e162b1a14a5bd860a464ab4e07513916a64ab1683fae262f735ccd2/numpy-2.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390", size = 17323429, upload-time = "2026-01-31T23:10:39.704Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/a2/73014149ff250628df72c58204822ac01d768697913881aacf839ff78680/numpy-2.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413", size = 18635109, upload-time = "2026-01-31T23:10:41.924Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/0c/73e8be2f1accd56df74abc1c5e18527822067dced5ec0861b5bb882c2ce0/numpy-2.4.2-cp311-cp311-win32.whl", hash = "sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda", size = 6237915, upload-time = "2026-01-31T23:10:45.26Z" },
+ { url = "https://files.pythonhosted.org/packages/76/ae/e0265e0163cf127c24c3969d29f1c4c64551a1e375d95a13d32eab25d364/numpy-2.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695", size = 12607972, upload-time = "2026-01-31T23:10:47.021Z" },
+ { url = "https://files.pythonhosted.org/packages/29/a5/c43029af9b8014d6ea157f192652c50042e8911f4300f8f6ed3336bf437f/numpy-2.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3", size = 10485763, upload-time = "2026-01-31T23:10:50.087Z" },
+ { url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/33/2eb97c8a77daaba34eaa3fa7241a14ac5f51c46a6bd5911361b644c4a1e2/numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", size = 6550820, upload-time = "2026-01-31T23:10:59.429Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/91/b97fdfd12dc75b02c44e26c6638241cc004d4079a0321a69c62f51470c4c/numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", size = 15663067, upload-time = "2026-01-31T23:11:01.291Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/c6/a18e59f3f0b8071cc85cbc8d80cd02d68aa9710170b2553a117203d46936/numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", size = 16619782, upload-time = "2026-01-31T23:11:03.669Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/83/9751502164601a79e18847309f5ceec0b1446d7b6aa12305759b72cf98b2/numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", size = 17013128, upload-time = "2026-01-31T23:11:05.913Z" },
+ { url = "https://files.pythonhosted.org/packages/61/c4/c4066322256ec740acc1c8923a10047818691d2f8aec254798f3dd90f5f2/numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", size = 18345324, upload-time = "2026-01-31T23:11:08.248Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/af/6157aa6da728fa4525a755bfad486ae7e3f76d4c1864138003eb84328497/numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", size = 5960282, upload-time = "2026-01-31T23:11:10.497Z" },
+ { url = "https://files.pythonhosted.org/packages/92/0f/7ceaaeaacb40567071e94dbf2c9480c0ae453d5bb4f52bea3892c39dc83c/numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", size = 12314210, upload-time = "2026-01-31T23:11:12.176Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/a3/56c5c604fae6dd40fa2ed3040d005fca97e91bd320d232ac9931d77ba13c/numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", size = 10220171, upload-time = "2026-01-31T23:11:14.684Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" },
+ { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" },
+ { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" },
+ { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" },
+ { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" },
+ { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" },
+ { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" },
+ { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" },
+ { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" },
+ { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" },
+ { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" },
+ { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" },
+ { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" },
+ { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" },
+ { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" },
+ { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" },
+ { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" },
+ { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" },
+ { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" },
+ { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" },
+ { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" },
+ { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" },
+ { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" },
+ { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" },
+ { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" },
+ { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f8/50e14d36d915ef64d8f8bc4a087fc8264d82c785eda6711f80ab7e620335/numpy-2.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082", size = 16833179, upload-time = "2026-01-31T23:12:53.5Z" },
+ { url = "https://files.pythonhosted.org/packages/17/17/809b5cad63812058a8189e91a1e2d55a5a18fd04611dbad244e8aeae465c/numpy-2.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a", size = 14889755, upload-time = "2026-01-31T23:12:55.933Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ea/181b9bcf7627fc8371720316c24db888dcb9829b1c0270abf3d288b2e29b/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920", size = 5399500, upload-time = "2026-01-31T23:12:58.671Z" },
+ { url = "https://files.pythonhosted.org/packages/33/9f/413adf3fc955541ff5536b78fcf0754680b3c6d95103230252a2c9408d23/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821", size = 6714252, upload-time = "2026-01-31T23:13:00.518Z" },
+ { url = "https://files.pythonhosted.org/packages/91/da/643aad274e29ccbdf42ecd94dafe524b81c87bcb56b83872d54827f10543/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb", size = 15797142, upload-time = "2026-01-31T23:13:02.219Z" },
+ { url = "https://files.pythonhosted.org/packages/66/27/965b8525e9cb5dc16481b30a1b3c21e50c7ebf6e9dbd48d0c4d0d5089c7e/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0", size = 16727979, upload-time = "2026-01-31T23:13:04.62Z" },
+ { url = "https://files.pythonhosted.org/packages/de/e5/b7d20451657664b07986c2f6e3be564433f5dcaf3482d68eaecd79afaf03/numpy-2.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0", size = 12502577, upload-time = "2026-01-31T23:13:07.08Z" },
+]
+
+[[package]]
+name = "packaging"
+version = "26.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
+]
+
+[[package]]
+name = "psutil"
+version = "7.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" },
+ { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" },
+ { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" },
+ { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" },
+ { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" },
+ { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" },
+ { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" },
+ { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
+]
+
+[[package]]
+name = "pycparser"
+version = "3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
+]
+
+[[package]]
+name = "pytest"
+version = "9.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "pygments" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
+]
+
+[[package]]
+name = "pythonnet"
+source = { editable = "." }
+dependencies = [
+ { name = "clr-loader" },
+]
+
+[package.dev-dependencies]
+dev = [
+ { name = "find-libpython" },
+ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
+ { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
+ { name = "psutil" },
+ { name = "pytest" },
+]
+
+[package.metadata]
+requires-dist = [{ name = "clr-loader", specifier = ">=0.3.0,<0.4.0" }]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "find-libpython", specifier = ">=0.3" },
+ { name = "numpy", marker = "python_full_version < '3.10'", specifier = "<2" },
+ { name = "numpy", marker = "python_full_version >= '3.10'", specifier = ">=2" },
+ { name = "psutil" },
+ { name = "pytest", specifier = ">=6" },
+]
+
+[[package]]
+name = "tomli"
+version = "2.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" },
+ { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" },
+ { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" },
+ { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" },
+ { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" },
+ { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" },
+ { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" },
+ { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" },
+ { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" },
+ { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" },
+ { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" },
+ { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" },
+ { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" },
+ { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" },
+ { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" },
+ { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
+]
diff --git a/version.txt b/version.txt
index 0f9d6b15d..a416f3693 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-3.1.0-dev
+3.1.0-rc.0