Posts: 58
Threads: 25
Joined: Jun 2023
06-17-2023, 09:16 PM
(This post was last modified: 08-01-2023, 08:00 PM by burque505.)
The PythonNET documentation available on the web can be frustratingly opaque. This example is cribbed from the one at pythonnet.github.io with significant tweaking.
/*/ nuget PyNet\pythonnet; /*/
using System;
using Python.Runtime;
// create a person object
Person person = new Person("Engelbert", "Humperdinck");
// If you do not have PYTHONNET_DLL set, uncomment the line below and
// modify the path to suit your system.
// Runtime.PythonDLL = @"C:\Program Files\Python310\python310.dll";
// I don't require that line on my system.
PythonEngine.Initialize();
// acquire the GIL before using the Python interpreter
using (Py.GIL())
{
// create a Python scope
using dynamic scope = Py.CreateScope();
{
// convert the Person object to a PyObject
PyObject pyPerson = person.ToPython();
// create a Python variable "person"
scope.Set("person", pyPerson);
// the person object may now be used in Python
scope.Exec("fullName = person.FirstName + ' ' + person.LastName");
var pythonStrObj = scope.Eval("fullName");
var csharpStrObj = pythonStrObj.As<string>();
print.it(csharpStrObj );
}
}
PythonEngine.Shutdown();
/// <summary>
/// C# Person class
/// </summary>
public class Person
{
/// <summary>
///
/// </summary>
/// <param name="firstName"></param>
/// <param name="lastName"></param>
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
/// <summary>
/// In the Python code, use FirstName
/// </summary>
public string FirstName { get; set; }
/// <summary>
/// In the Python code, use LastName
/// </summary>
public string LastName { get; set; }
}
Here's another example, this time converting a Python list object to a C# int[]. I got the original example from this CodeProject post, and it was a little easier to mod.
/*/ nuget PyNet\PythonNet; /*/
using Python.Runtime;
PythonEngine.Initialize();
using (Py.GIL()) // begin 'using'
{
using var scope = Py.CreateScope();
scope.Exec("number_list = [1, 2, 3, 4, 5]");
var pythonListObj = scope.Eval("number_list");
var csharpListObj = pythonListObj.As<int[]>();
print.it("The numbers from python are:");
foreach (var value in csharpListObj)
{
print.it(value);
}
} // end 'using'.
PythonEngine.Shutdown();
Posts: 58
Threads: 25
Joined: Jun 2023
06-17-2023, 10:23 PM
(This post was last modified: 08-01-2023, 07:58 PM by burque505.)
Another example.
- Get first number from the user (as string).
- Convert to double.
- Get second number from the user (as string).
- Convert to double.
- Create Python scope.
- In scope, define a Python function 'add'.
- Create C# var 'sum', equal to the return value of scope.add(firstNumber, secondNumber).
- Print 'sum'.
/*/ nuget PyNet\PythonNet; /*/
using Python.Runtime;
PythonEngine.Initialize();
using (Py.GIL()) {
// NOTE: this doesn't validate input
if (!dialog.showInput(out string firstCsharpNumber, "Enter first number:")) return;
PyObject firstNumber = Convert.ToDouble(firstCsharpNumber).ToPython();
if (!dialog.showInput(out string secondCsharpNumber, "Enter second number:")) return;
PyObject secondNumber = Convert.ToDouble(secondCsharpNumber).ToPython();
dynamic scope = Py.CreateScope();
scope.Exec("def add(a, b): return a + b");
var sum = scope.add(firstNumber, secondNumber);
print.it("Sum: " + sum);
}
PythonEngine.Shutdown();
Posts: 12,190
Threads: 144
Joined: Dec 2002
06-18-2023, 05:58 AM
(This post was last modified: 06-18-2023, 06:07 AM by Gintaras.)
Insert before PythonEngine.Initialize():
Runtime.PythonDLL = @"C:\Program Files\Python311\python311.dll";
Or set environment variable PYTHONNET_PYDLL.
If Python not installed, download from https://www.python.org/downloads/
Posts: 58
Threads: 25
Joined: Jun 2023
06-18-2023, 12:27 PM
(This post was last modified: 06-18-2023, 12:32 PM by burque505.
Edit Reason: Add (or similar)
)
@Gintaras, thank you. I should have mentioned that my PYTHONNET_DLL is already set. Many of the examples I've looked at have the line you posted (or similar) in the code. OP has been updated.
Regards,
burque505
Posts: 12,190
Threads: 144
Joined: Dec 2002
The script process does not exit. Finally need to call
Posts: 12,190
Threads: 144
Joined: Dec 2002
08-01-2023, 06:40 PM
(This post was last modified: 05-19-2025, 04:28 PM by Gintaras.)
This class makes it simpler.
// class "Pynet.cs"
/*/ nuget PyNet\PythonNet; /*/
using Python.Runtime;
/// <summary>
/// Initializes the Python engine, locks its global interpreter and adds scope.
/// Optionally adds Python code; then you can call functions etc.
/// When disposing, unlocks/disposes objects and shuts down the engine.
/// </summary>
public class Pynet : IDisposable {
Py.GILState _gil;
PyModule _m;
bool _shutdown;
/// <summary>
/// Initializes the Python engine and optionally adds code.
/// </summary>
/// <param name="code">If not null/"", calls <b>PyModule.Exec</b>.</param>
/// <param name="enablePrint">Redirect the output of the Python's print function to console. Default true.</param>
public Pynet(string code = null, bool enablePrint = true) {
if (!PythonEngine.IsInitialized) {
if (Environment.GetEnvironmentVariable("PYTHONNET_PYDLL").NE()) Runtime.PythonDLL = @"C:\Program Files\Python311\python311.dll"; //edit this if need. Or set the environment variable.
PythonEngine.Initialize();
_shutdown = true;
//process.thisProcessExit += _ => PythonEngine.Shutdown(); //does not work
}
_gil = Py.GIL();
if (enablePrint) {
Module.Exec("""
import sys
class NetConsole(object):
def __init__(self, writeCallback):
self.writeCallback = writeCallback
def write(self, message):
self.writeCallback(message)
def flush(self):
pass
def setConsoleOut(writeCallback):
sys.stdout = NetConsole(writeCallback)
""");
var writer = (string s) => { Console.Write(s); };
Module.InvokeMethod("setConsoleOut", Python.Runtime.PyObject.FromManagedObject(writer));
}
if (!code.NE()) Module.Exec(code);
}
/// <summary>
/// Unlocks/disposes Python objects and shuts down the engine.
/// </summary>
public void Dispose() {
_m?.Dispose();
_gil?.Dispose();
if (_shutdown) {
try { PythonEngine.Shutdown(); } //without this the process does not exit
catch (System.Runtime.Serialization.SerializationException) { } //thrown when using enablePrint
catch (PlatformNotSupportedException) { } //.NET 9+: BinaryFormatter serialization and deserialization have been removed
}
}
/// <summary>
/// Gets the result of <b>Py.CreateScope</b>.
/// You can assign it to a <c>dynamic</c> variable and call functions defined in your Python code.
/// </summary>
public PyModule Module => _m ??= Py.CreateScope();
}
Examples.
// script "Pynet examples.cs"
/*/ c Pynet.cs; /*/
using Python.Runtime;
#if true
string code = """
def Multi(a1, a2):
return a1 * a2
def JoinStr(a1, a2):
return a1 + a2
""";
using var pyn = new Pynet(code);
dynamic m = pyn.Module;
double d1 = m.Multi(2, 3.5);
string s1 = m.JoinStr("joi", "ned");
print.it(d1, s1);
#elif !true
using var pyn = new Pynet();
dynamic mod = Py.Import("math");
print.it(mod.cos(mod.pi * 2));
#elif !true
using var pyn = new Pynet();
print.it(PythonEngine.Eval("3 + 4"));
#elif !true
string code = """
import ctypes
ctypes.windll.user32.MessageBoxW(0, "Text", "Title", 1)
""";
using var pyn = new Pynet();
PythonEngine.RunSimpleString(code);
#endif
Posts: 58
Threads: 25
Joined: Jun 2023
Thank you, Gintaras, I'll try it out.
Posts: 1,133
Threads: 262
Joined: Jul 2022
01-29-2024, 01:10 AM
(This post was last modified: 01-29-2024, 01:11 AM by Davider.)
The print statements in Python are not producing output. I've seen in some articles that it's possible to output them in Visual Studio. This can be particularly useful for functions that return void values.
string code = """
print("hello world from python!")
""";
using var pyn = new Pynet();
PythonEngine.RunSimpleString(code);
Posts: 12,190
Threads: 144
Joined: Dec 2002
01-29-2024, 09:15 AM
(This post was last modified: 01-29-2024, 09:17 AM by Gintaras.)
I updated the Pynet code, now it by default enables Python's print.
Posts: 1,133
Threads: 262
Joined: Jul 2022
Posts: 58
Threads: 25
Joined: Jun 2023
05-16-2025, 09:41 PM
(This post was last modified: 05-16-2025, 09:42 PM by burque505.)
I have run into problems with PythonNET recently following the upgrade from C# 8 to C#9 (I think). I've had this error code pop up (actually in other C# projects as well):
System.PlatformNotSupportedException: BinaryFormatter serialization and deserialization have been removed. See https://aka.ms/binaryformatter for more information.
I believe there were severe security issues with the BinaryFormatter that resulted in it being removed. At the moment, I've simply been deleting references to 'PythonEngine.Shutdown()' from @Gintaras's class, and the error goes away. I still use the following, however:
PythonEngine.Initialize();
using (Py.GIL()) {
// do stuff
dynamic scope = Py.CreateScope();
// do stuff with scope
Py.GIL().Dispose();
}
@Gintaras may have a better way to do this, of course.
Best regards,
burque505
Posts: 12,190
Threads: 144
Joined: Dec 2002
Catch the exception.
public void Dispose() {
_m?.Dispose();
_gil?.Dispose();
if (_shutdown) {
try { PythonEngine.Shutdown(); } //without this the process does not exit
catch (System.Runtime.Serialization.SerializationException) { } //thrown when using enablePrint
catch (PlatformNotSupportedException) { } //.NET 9+: BinaryFormatter serialization and deserialization have been removed
}
}
https://github.com/pythonnet/pythonnet/issues/2469
Posts: 1,133
Threads: 262
Joined: Jul 2022
06-09-2025, 08:39 AM
(This post was last modified: 06-09-2025, 08:56 AM by Davider.)
no out, no tip
// script "Pynet examples4.cs"
/*/ c Pynet.cs; /*/
using Python.Runtime;
string code = """
import win32print
print(win32print.__file__)
# out print name
default_printer = win32print.GetDefaultPrinter()
print(default_printer)
""";
using var pyn = new Pynet();
PythonEngine.RunSimpleString(code);
The following code produces output indicating that the module was not found, but the module is already installed.
using var pyn = new Pynet(code);
For multi-line Python code that is not defined as a function, the above method of calling it is not concise enough.
How to know if the code executed successfully?
Posts: 12,190
Threads: 144
Joined: Dec 2002
Output here:
C:\Program Files\Python311\Lib\site-packages\win32\win32print.pyd
Microsoft Print to PDF
Quote:How to know if the code executed successfully?
Probably `PythonEngine.RunSimpleString`does not report exceptions. Instead use
Posts: 1,133
Threads: 262
Joined: Jul 2022
06-09-2025, 09:56 AM
(This post was last modified: 06-09-2025, 11:23 AM by Davider.)
Thanks for your help!
I successfully tested the above code on a fresh Win10 VM with a clean installation of Python 3.12 and LA.
So it's possible that I messed up my environment variables.
|