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/Justfile b/Justfile new file mode 100644 index 000000000..b5228e2e0 --- /dev/null +++ b/Justfile @@ -0,0 +1,14 @@ +default: + @just --choose + +setup: + dotnet restore + uv sync + +docs: + doxygen doc/Doxyfile + uv run --group doc sphinx-build doc/source/ ./doc/build/html/ + +build-wheels: + uv build + uv build --wheel -C="--global-option=--net46-support" \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index 71473c2c3..1d4889e25 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,7 @@ graft src/runtime prune src/runtime/obj prune src/runtime/bin +graft src/compat include src/pythonnet.snk include Directory.Build.* include pythonnet.sln diff --git a/doc/source/python.rst b/doc/source/python.rst index a9228537c..5bc39accb 100644 --- a/doc/source/python.rst +++ b/doc/source/python.rst @@ -45,7 +45,8 @@ 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. For .NET 4.6 support, the wheel has + to be built with the environment variable `PYTHONNET_BUILD_NET46_SUPPORT=1`. .NET Core (``coreclr``) Self-contained is not supported, must be at least version 3.1. diff --git a/pyproject.toml b/pyproject.toml index 75df0f072..a270163ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,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..c0683b000 100644 --- a/setup.py +++ b/setup.py @@ -3,15 +3,20 @@ import distutils from distutils.command.build import build as _build from setuptools.command.develop import develop as _develop -from wheel.bdist_wheel import bdist_wheel as _bdist_wheel from setuptools import Distribution from setuptools import setup, Command import os +import sys # Disable SourceLink during the build until it can read repo-format v1, #1613 os.environ["EnableSourceControlManagerQueries"] = "false" +NET46_SUPPORT_OPTION = "--net46-support" +NET46_SUPPORT = NET46_SUPPORT_OPTION in sys.argv +if NET46_SUPPORT: + sys.argv.remove(NET46_SUPPORT_OPTION) + class DotnetLib: def __init__(self, name, path, **kwargs): @@ -106,14 +111,6 @@ def install_for_development(self): return super().install_for_development() -class bdist_wheel(_bdist_wheel): - def finalize_options(self): - # Monkey patch bdist_wheel to think the package is pure even though we - # include DLLs - super().finalize_options() - self.root_is_pure = True - - # Monkey-patch Distribution s.t. it supports the dotnet_libs attribute Distribution.dotnet_libs = None @@ -121,18 +118,20 @@ def finalize_options(self): "build": build, "build_dotnet": build_dotnet, "develop": develop, - "bdist_wheel": bdist_wheel, } -dotnet_libs = [ - DotnetLib( - "python-runtime", - "src/runtime/Python.Runtime.csproj", - output="pythonnet/runtime", - ) -] + +if NET46_SUPPORT: + csproj = "src/compat/Python.Runtime.Compat.csproj" + plat_name = "win32" +else: + csproj = "src/runtime/Python.Runtime.csproj" + plat_name = "any" + +dotnet_libs = [DotnetLib("python-runtime", csproj, output="pythonnet/runtime")] setup( cmdclass=cmdclass, dotnet_libs=dotnet_libs, + options={"bdist_wheel": {"plat_name": plat_name}}, ) 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..08c0be7cb 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,28 @@ 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. + // Only needed on .NET Framework; on .NET (Core) this causes + // duplicate assembly loads, as .deps.json is respected and + // the correct assembly is already found. + if (typeof(object).Assembly.GetName().Name == "mscorlib") + { + 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))