You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
BinaryFormatter serialization and deserialization warning which throws an error in .net8 #2282
When shutting down the the PythonEngine via a Dispose method on the impermentation class, the error occurs at the PythonEngine.Shutdown(); method and throws the error System.NotSupportedException: BinaryFormatter serialization and deserialization are disabled within this application (see trace below). The same code worked in .net7 without any issues, the error message leads to this description
_logger.Log(LogLevel.Error, "Shutting down Python engine"); runs
it crashes at the PythonEngine.Shutdown(); method
_logger.Log(LogLevel.Error, "Python engine shut down"); does not run
private void Dispose(bool disposing)
{
_logger.Log(LogLevel.Error, "Dispose");
if (_disposed) return;
if (disposing)
{
_logger.Log(LogLevel.Error, "Shutting down Python engine");
PythonEngine.Shutdown();
_logger.Log(LogLevel.Error, "Python engine shut down");
}
_disposed = true;
}
print('TODO')
System.NotSupportedException: BinaryFormatter serialization and deserialization are disabled within this application. See https://aka.ms/binaryformatt...
System.NotSupportedException
BinaryFormatter serialization and deserialization are disabled within this application. See https://aka.ms/binaryformatter for more information.
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
at Python.Runtime.RuntimeData.Stash()
at Python.Runtime.Runtime.Shutdown()
at Python.Runtime.PythonEngine.Shutdown()
at EcgAi.Training.Infrastructure.Python.PythonEnvironment.Dispose(Boolean disposing) in C:\Data\Rider\EcgAi.Training\Src\EcgAi.Training.Infrastructure.Python\PythonEnvironment.cs:line 113
at EcgAi.Training.Infrastructure.Python.PythonEnvironment.Dispose() in xxxxxxxxxxxxxxxxxxxxxxxxx
This is a big issue as the binary formatter was deprecated as it is a serious security issue.
To replace the formatter, there looks to be two high performance/low latency alternatives... Protobuf and MessagePack. Any preference which to use (MessagePack will be slightly faster than Protobuf, but Protobuf is more well known)?
No formatter is really needed unless you are using .NET Framework. The only place where formatter is used is app domain restart, and app domains are not available in later .NETs
All I had to do was implement that PluginLoadContext class... (note that I changed it ever so slightly from the example in the linked article to enable unloading which is off by default, because of its associated cost)
classPluginLoadContext:AssemblyLoadContext{privateAssemblyDependencyResolver_resolver;publicPluginLoadContext(stringpluginPath):base(true)// passing true to the base constructor enables unloading{_resolver=newAssemblyDependencyResolver(pluginPath);}protectedoverrideAssemblyLoad(AssemblyNameassemblyName){stringassemblyPath=_resolver.ResolveAssemblyToPath(assemblyName);if(assemblyPath!=null){returnLoadFromAssemblyPath(assemblyPath);}returnnull;}protectedoverrideIntPtrLoadUnmanagedDll(stringunmanagedDllName){stringlibraryPath=_resolver.ResolveUnmanagedDllToPath(unmanagedDllName);if(libraryPath!=null){returnLoadUnmanagedDllFromPath(libraryPath);}returnIntPtr.Zero;}}
...and then change up my code a bit to have a PluginLoadContext instance and use that to load the assembly and unload it (using IDisposable), and I was finally able to keep reusing the same assembly, without pythonnet throwing an exception that it's still initialized when I would invoke the assembly's constructor on the very first Runtime.PythonDLL = ... line.
publicclassPythonTTSProvider:ITTSProvider// ITTSProvider : IDisposable{privatereadonlydynamic_pythonTTSLib;privatestaticPluginLoadContext?_pluginLoadContext;privatePythonTTSProvider(dynamicpythonTTSLib){_pythonTTSLib=pythonTTSLib;// ...}publicstaticITTSProvider?New(){stringassemblyPath=Path.Combine(AppDomain.CurrentDomain.BaseDirectory,@"PythonTTS\PythonTTS.dll");_pluginLoadContext=new(assemblyPath);dynamic?ttsLib=null;if(File.Exists(assemblyPath)){varassembly=_pluginLoadContext.LoadFromAssemblyPath(assemblyPath);vartype=assembly.GetType("PythonTTS.KokoroTTS");if(type!=null){ttsLib=Activator.CreateInstance(type,[Path.Combine(AppDomain.CurrentDomain.BaseDirectory,@"PythonTTS\python3.12.9\python312.dll")]);}}else{thrownewFileNotFoundException($"PythonTTS library not found. Please ensure you have extracted the PythonTTS archive.\n\nFile path expected: {assemblyPath}");}returnttsLib!=null?newPythonTTSProvider(ttsLib):null;}// ...publicvoidDispose(){_pluginLoadContext?.Unload();GC.SuppressFinalize(this);}
I was so happy to finally get this to work after 2 days of struggling with it.
I created a custom JsonBinaryFormater, based on good old Newtonsoft.Json for this issue:
public class JsonBinaryFormatter : IFormatter
{
private readonly JsonSerializer_serializer;
public JsonFormatter()
{
_serializer = new JsonSerializer();
}
/// <inheritdoc />
public SerializationBinder Binder { get; set; }
/// <inheritdoc />
public StreamingContext Context { get; set; }
/// <inheritdoc />
public ISurrogateSelector SurrogateSelector { get; set; }
/// <inheritdoc />
public object Deserialize(Stream serializationStream)
{
using var sr = new StreamReader(stream, Encoding.UTF8, true, 1024, true);
using var jsonTextReader = new JsonTextReader(sr);
return _serializer.Deserialize(jsonTextReader);
}
/// <inheritdoc />
public void Serialize(Stream serializationStream, object graph)
{
using var sr = new StreamWriter(serializationStream, Encoding.UTF8, 1024, true);
_serializer.Serialize(sr, graph);
}
}
This is not a true solution, but to silence the exception you can just override the formatter with NoopFormatter:
Python.Runtime.RuntimeData.FormatterType = typeof(Python.Runtime.NoopFormatter);
Activity
TimIrwin26 commentedon Dec 17, 2023
The issue happens in RuntimeData.Stash --
While there is an option to set another formatter, it's not used and expected to be of type IFormatter which is problematic.
fsz1987 commentedon Jan 1, 2024
Pythonnet use BinaryFormatter. and it is deprecated in .net 8.
create an instance of BinaryFormatter will cause an error.
you can set RuntimeData.FormatterType to your custom formatter to get it work.
if you dont want write a custom formatter, then add this line
before you call PythonEngine.Shutdown to temporary enable BinaryFormatter.
after PythonEngine.Shutdown. call
to disable BinaryFormatter
amishratnasthapit commentedon Feb 19, 2024
Facing same issue and using work around currently. But .NET 9 is getting rid of Binary Formatter altogether. So this needs to be fixed.
Avoid shutdown exception due to deprecated API
AtwoodTM commentedon Aug 31, 2024
This is a big issue as the binary formatter was deprecated as it is a serious security issue.
To replace the formatter, there looks to be two high performance/low latency alternatives... Protobuf and MessagePack. Any preference which to use (MessagePack will be slightly faster than Protobuf, but Protobuf is more well known)?
lostmsu commentedon Aug 31, 2024
No formatter is really needed unless you are using .NET Framework. The only place where formatter is used is app domain restart, and app domains are not available in later .NETs
filmor commentedon Sep 19, 2024
This should be fixed now.
nwoolls commentedon Oct 21, 2024
@filmor is this fix available on NuGet? I'm still seeing this issue with the latest 3.1 preview version on NuGet.
Lemur8063 commentedon Nov 1, 2024
no
BinaryFormatter serialization - security risk - discussion: the Pytho…
Baumx1234 commentedon Dec 17, 2024
I'm using Nuget Version 3.0.5 and .Net Version 8.0 and still got the issue.
filmor commentedon Dec 17, 2024
Yeah, this is more difficult to fix than anticipated.
WallucePinkham commentedon Jan 27, 2025
Is there an update on this issues, by any chance?
leftos commentedon Feb 10, 2025
I was struggling with this, tried to use all sorts of BinaryFormatter NuGet packages to get pythonnet to be happy, and nothing worked...
But then I came across this:
https://learn.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support
All I had to do was implement that PluginLoadContext class...
(note that I changed it ever so slightly from the example in the linked article to enable unloading which is off by default, because of its associated cost)
...and then change up my code a bit to have a PluginLoadContext instance and use that to load the assembly and unload it (using IDisposable), and I was finally able to keep reusing the same assembly, without pythonnet throwing an exception that it's still initialized when I would invoke the assembly's constructor on the very first
Runtime.PythonDLL = ...line.I was so happy to finally get this to work after 2 days of struggling with it.
Hopefully this helps someone else as well.
sharpSteff commentedon Feb 11, 2025
I created a custom JsonBinaryFormater, based on good old Newtonsoft.Json for this issue:
It can be used via
Python.Runtime.RuntimeData.FormatterType = typeof(JsonFormatter);So far I have no issues
novitk commentedon Aug 3, 2025
This is not a true solution, but to silence the exception you can just override the formatter with NoopFormatter:
Python.Runtime.RuntimeData.FormatterType = typeof(Python.Runtime.NoopFormatter);