henon / Python.Included

A Python.NET based framework enabling .NET libraries to call into Python packages without depending on a local Python installation.
MIT License
313 stars 51 forks source link

Problem with WinUI3 Project #39

Open onslauth opened 2 years ago

onslauth commented 2 years ago

Hi,

I have created a project using the new WinUI3 Template in Visual Studio 2022: image

Trying to use Python.Included, I receive the following error in the debug output:

Exception thrown at 0x00007FFF99A34FD9 in WinUI3PythonIncluded.exe: Microsoft C++ exception: EEMessageException at memory location 0x000000DF2519D450.
Exception thrown: 'System.DllNotFoundException' in Python.Runtime.dll

The project can be found here https://github.com/onslauth/WinUI3PythonIncluded.

Creating a normal console application with Python.Included works fine.

Do you perhaps have any suggestions that I can try to resolve the problem?

henon commented 2 years ago

Try if you can get pythonnet to work directly (Python.Included is just a deployment solution for pythonnet). See if it throws the same exception. That should give you more insights

onslauth commented 2 years ago

Are you talking about

https://github.com/henon/pythonnet_netstandard or https://github.com/pythonnet/pythonnet ?

onslauth commented 2 years ago

If I understand you correctly, I want to be looking at the following file: https://github.com/henon/Python.Included/blob/master/Python.Included/Installer.cs

Is that correct?

henon commented 2 years ago

yes I am talking about https://github.com/pythonnet/pythonnet

see if it works with WinUI3

henon commented 2 years ago

If I understand you correctly, I want to be looking at the following file: https://github.com/henon/Python.Included/blob/master/Python.Included/Installer.cs

Is that correct?

Only if we assume that the deployment didn't work. But there is no indication of that yet.

onslauth commented 2 years ago

I have installed the embedded python to the folder C:\Python\

I have also created a new WinUI3 template program to test the code and tested both inside a task and without:

        private async Task TestPython( )
        {
            using ( Py.GIL( ) )
            {
                dynamic sys = Py.Import( "sys" );
            }
        }

        private void myButton_Click( object sender, RoutedEventArgs e )
        {
            myButton.Content = "Clicked";
            Runtime.PythonDLL = "C:\\Python\\python310.dll";

            Task.Run( ( ) => this.TestPython( ) );
        }

and

        private void myButton_Click( object sender, RoutedEventArgs e )
        {
            myButton.Content = "Clicked";
            Runtime.PythonDLL = "C:\\Python\\python310.dll";

            using ( Py.GIL( ) )
            {
                dynamic sys = Py.Import( "sys" );
            }
        }

They both fail to run with the following errors respectively:

'WinUI3Pythonnet.exe' (Win32): Loaded 'C:\python\python310.dll'. 
'WinUI3Pythonnet.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140.dll'. 
Exception thrown at 0x00007FFE9C055F68 (python310.dll) in WinUI3Pythonnet.exe: 0xC0000005: Access violation reading location 0x0000000000000010.
'WinUI3Pythonnet.exe' (Win32): Loaded 'C:\python\python310.dll'. 
'WinUI3Pythonnet.exe' (Win32): Loaded 'C:\Windows\System32\vcruntime140.dll'. 
Exception thrown at 0x00007FFE9B25FA7D (python310.dll) in WinUI3Pythonnet.exe: 0xC0000005: Access violation reading location 0x0000000000000011.

I expect I need to take this up further with the pythonnet developers?

henon commented 2 years ago

Yes. Please post the link to issue you are about to create on their github here so other people with similar issues can follow.

onslauth commented 2 years ago

https://github.com/pythonnet/pythonnet/issues/1796

Thank you for the help.

onslauth commented 2 years ago

With help from the Pythonnet developers, the program now runs.

The solution can be found here: https://github.com/onslauth/WinUI3-Pythonnet

Is there something else you would like me to try with regards to getting Python.Included working?

onslauth commented 2 years ago

I've had some time to play around now.

I updated the nuget package to: 3.10.0-preview1 so that I can directly set the PythonDLL property.

During Installer.SetupPython( ) it seems to crash or hang or something in this step breaks.

I changed my code to the following:

            Installer.InstallPath = @"C:\Testing\";
            string python_dll_path = Path.Combine( Installer.EmbeddedPythonHome, "python310.dll" );
            var task = Installer.SetupPython( );
            Task.WhenAll( task );

            Runtime.PythonDLL = python_dll_path;

            PythonEngine.Initialize( );
            using ( Py.GIL( ) )
            {
                dynamic sys = Py.Import( "sys" );
                string version = sys.version;
                System.Diagnostics.Debug.WriteLine( "VERSION: {0}", version, null );
            }

When I run the program for the first time when the C:\Testing directory is empty, it will extract the zip file to the directory, and create the directory python-3.10.0-embed-amd64. However it crashes at this point and even manages to crash the Visual Studio Debugger. It also appears that something happened during extraction:

image

If I extract the contents of the zip file python-3.10.0-embed-amd64.zip into C:\Testing\python-3.10.0-embed-amd64 and run the program again, everything now works correctly:

VERSION: 3.10.0 (tags/v3.10.0:b494f59, Oct  4 2021, 19:00:18) [MSC v.1929 64 bit (AMD64)]
henon commented 2 years ago

If I were you I would check out the code of SetupPython and single step it in debugger to see exactly which line crashes.

onslauth commented 2 years ago

Hi,

I have managed to track the problem down to line 92 in Python.Deployment Installer.cs, specifically this line:

ZipFile.ExtractToDirectory(zip, zip.Replace(".zip", "") );

Even though the code is in a try/catch section, it seems to bypass that.

I have also tested the above code in a new WinUI 3 Template project, and everything works fine without issue:

        private async void myButton_Click( object sender, RoutedEventArgs e )
        {
            myButton.Content = "Clicked";

            var zip_file = "C:\\Testing\\python-3.10.0-embed-amd64.zip";
            string zip_path = "C:\\Testing\\python-3.10.0-embed-amd64";
            await Task.Run( ( ) =>
            {
                try
                {
                    ZipFile.ExtractToDirectory( zip_file, zip_file.Replace( ".zip", "" ) );
                }
                catch ( Exception e )
                {
                    System.Diagnostics.Debug.WriteLine( "\n\n\n{}\n\n\n", e, null );
                }
            } );

        }

If I then run the WinUI3 project with Python.Included in it, after running the above project and letting it extract the zip file, everything works correctly.

Is there anything else you would like me to try or test?

henon commented 2 years ago

I have no idea why the same line of code works in your app but not when called via the nuget or link the python included code base.

onslauth commented 2 years ago

Hi @henon

I have had some time to play with the code, and found a solution for you.

The following seems to work without problems:

            var task = Task.Run( ( ) =>
            {
                try
                {
                    ZipFile.ExtractToDirectory( zip, zip.Replace( ".zip", "" ) );

                    var pth = Path.Combine(EmbeddedPythonHome, Source.GetPythonVersion() + "._pth");
                    File.Delete(pth);
                }
                catch ( Exception e )
                {
                    Log("SetupPython: Error extracting zip file: " + zip);
                }
            } );
            task.Wait( );

Please let me know if you require any further testing from me.

henon commented 2 years ago

Can you explain why your change works?

onslauth commented 2 years ago

Sorry, I have absolutely no idea. I've only been writing C# for about 2 months.

I also tried the following:

        await Task.WhenAll( task );

Anything with the await seems to cause it to break.

Hope that helps.

One last thing I noticed when going through the code for the main branch, you haven't updated the download URL to 3.10. In Python.Deployment -> Installer.cs:52:

public static InstallationSource Source { get; set; } = new DownloadInstallationSource() { DownloadUrl = @"https://www.python.org/ftp/python/3.7.3/python-3.7.3-embed-amd64.zip" };

Thank you for all the help.

henon commented 2 years ago

and what if you call SetupPython().Wait(); ? wouldn't that work?

henon commented 2 years ago

by the way, there is a preview package including python 3.10: https://www.nuget.org/packages/Python.Included/3.10.0-preview2

onslauth commented 2 years ago

Hi,

Sorry, I've been a bit busy.

I will try the updated package and let you know. I see that you only changed the version number of the python used, and nothing further has been done regarding this issue?

henon commented 2 years ago

Yes I did not change anything else. Since this seems to be a problem with await in your app I see no reason to change SetupPython from async to blocking because you can call the SetupPython method blockingly yourself like this: SetupPython().Wait();

Where would be the difference between me calling Wait() inside of SetupPython and you calling it on your side? The effect should be the same. Please try and let me know.