diff --git a/AUTHORS.md b/AUTHORS.md index 7ea639059..96e58ff46 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -89,3 +89,4 @@ - Ehsan Iran-Nejad ([@eirannejad](https://github.com/eirannejad)) - ([@legomanww](https://github.com/legomanww)) - ([@gertdreyer](https://github.com/gertdreyer)) +- Kerbiter ([@Metadorius](https://github.com/Metadorius)) diff --git a/CHANGELOG.md b/CHANGELOG.md index df68fbb39..73a81368f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,12 +10,13 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. ### Added - Support `del obj[...]` for types derived from `IList` and `IDictionary` +- Support for .NET Framework 4.6.1 (#2701) ### Changed ### Fixed - Fixed crash when trying to `del clrObj[...]` for non-arrays -- ci: properly exclude job (#2542) +- ci: properly exclude job (#2542) ## [3.0.5](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.5) - 2024-12-13 diff --git a/doc/source/python.rst b/doc/source/python.rst index a9228537c..89f90eb07 100644 --- a/doc/source/python.rst +++ b/doc/source/python.rst @@ -45,7 +45,7 @@ Mono (``mono``) .NET Framework (``netfx``) Default on Windows and also only supported there. Must be at least version - 4.7.2. + 4.6.1, with 4.7.2 or later recommended. .NET Core (``coreclr``) Self-contained is not supported, must be at least version 3.1. diff --git a/pyproject.toml b/pyproject.toml index 59d4d107a..d9b26f63c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,9 @@ email = "pythonnet@python.org" Homepage = "https://pythonnet.github.io/" Sources = "https://github.com/pythonnet/pythonnet" +[project.entry-points.pyinstaller40] +hook-dirs = "pythonnet._pyinstaller:get_hook_dirs" + [tool.setuptools] zip-safe = false py-modules = ["clr"] diff --git a/pythonnet/_pyinstaller/__init__.py b/pythonnet/_pyinstaller/__init__.py new file mode 100644 index 000000000..2ed816a4e --- /dev/null +++ b/pythonnet/_pyinstaller/__init__.py @@ -0,0 +1,4 @@ +import os + +def get_hook_dirs(): + return [os.path.dirname(__file__)] diff --git a/pythonnet/_pyinstaller/hook-clr.py b/pythonnet/_pyinstaller/hook-clr.py new file mode 100644 index 000000000..f0b058681 --- /dev/null +++ b/pythonnet/_pyinstaller/hook-clr.py @@ -0,0 +1,9 @@ +from PyInstaller.utils.hooks import collect_data_files, collect_dynamic_libs + +try: + binaries = collect_dynamic_libs("pythonnet") + datas = collect_data_files("pythonnet") +except Exception: + # name conflict with https://pypi.org/project/clr/, do not crash if just clr is present + binaries = [] + datas = [] diff --git a/setup.py b/setup.py index 7c02b7710..678aeba2e 100644 --- a/setup.py +++ b/setup.py @@ -127,7 +127,7 @@ def finalize_options(self): dotnet_libs = [ DotnetLib( "python-runtime", - "src/runtime/Python.Runtime.csproj", + "src/compat/Python.Runtime.Compat.csproj", output="pythonnet/runtime", ) ] diff --git a/src/compat/Python.Runtime.Compat.csproj b/src/compat/Python.Runtime.Compat.csproj new file mode 100644 index 000000000..5c607e157 --- /dev/null +++ b/src/compat/Python.Runtime.Compat.csproj @@ -0,0 +1,13 @@ + + + + net461 + Library + + false + false + + + + + diff --git a/src/runtime/Loader.cs b/src/runtime/Loader.cs index c0e964abc..5f76a0c05 100644 --- a/src/runtime/Loader.cs +++ b/src/runtime/Loader.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Text; namespace Python.Runtime @@ -12,6 +13,22 @@ public unsafe static int Initialize(IntPtr data, int size) { try { + // On .NET Framework, the host is python.exe which has no binding + // redirects for netstandard2.0 shims (e.g. RuntimeInformation + // Version=0.0.0.0 vs the 4.0.2.0 shim on disk). Binding redirects + // via config files can't be injected after AppDomain creation, so + // resolve assemblies from our runtime directory directly. + AppDomain.CurrentDomain.AssemblyResolve += (_, args) => + { + var name = new System.Reflection.AssemblyName(args.Name); + var dir = Path.GetDirectoryName(typeof(Loader).Assembly.Location); + var path = Path.Combine(dir, name.Name + ".dll"); + + return File.Exists(path) + ? System.Reflection.Assembly.LoadFrom(path) + : null; + }; + var dllPath = Encodings.UTF8.GetString((byte*)data.ToPointer(), size); if (!string.IsNullOrEmpty(dllPath))