Embedding IronPython and overwriting Environment Variables - c#

I embed an IronPython interpreter in a C# application to execute a Python script. The script evaluates a specific environment variable VAR1 using os.environ['VAR1']
I need to change the value of VAR1 temporarily before the script is executed. To do this I call
Environment.SetEnvironmentVariable("VAR1", "NEW_VALUE");
in the C# code. Unfortunately this does not work. The Python script still "sees" the old value of VAR1 (the value it had when the hosting C# application started).
Here is a complete example code:
Environment.SetEnvironmentVariable("VAR1", "NEW_VALUE");
var engine = Python.CreateEngine();
engine.SetSearchPaths(new string[] { #"c:\Program Files (x86)\IronPython 2.7\Lib\" });
var ms = new MemoryStream();
engine.Runtime.IO.SetOutput(ms, Encoding.Unicode);
var script = engine.CreateScriptSourceFromString("import os\nprint os.environ['VAR1']");
script.Execute();
System.Windows.Forms.MessageBox.Show(Encoding.Unicode.GetString(ms.ToArray()));
My understanding is that the IronPython engine runs in the same process as the C# host application. If this is correct, how can the Python code and the C# code see different environment variables?
Is there a better (working) way to set an environment variable for the Python script?

Well, I still don't know why the above code doesn't work but one can simply set an environment variable in the Python environment by executing Python code that sets the variable BEFORE executing the real code:
engine.CreateScriptSourceFromString("import os\nos.environ['VAR1'] = 'NEW_VALUE'").Execute();
var CodeIReallyWantToRun = engine.CreateScriptSourceFromFile(...);
CodeIReallyWantToRun.Execute();

Related

IronPython in a C# application - SyntaxErrorException: 'invalid syntax'

I am trying to execute some python using IronPython in a C# WPF application. The code below works without the 'import os' line is removed. With that line I get a SyntaxErrorException: 'invalid syntax' error. I believe that is the correct syntax?
public MainWindow()
{
InitializeComponent();
ScriptEngine pythonEngine = Python.CreateEngine();
var paths = pythonEngine.GetSearchPaths();
paths.Add(#"C:\Python38\Lib");
pythonEngine.SetSearchPaths(paths);
ScriptScope scope = pythonEngine.CreateScope();
scope.SetVariable("App", this);
ScriptSource pythonScript = pythonEngine.CreateScriptSourceFromString(#"
import os
f = open('demofile4.txt', 'a')
f.write('Now the file has more content!')
f.close()");
pythonScript.Execute(scope);
}
I have this working now. IronPython isn't compatible with Python 3.8. From the website:
IronPython 3.4 uses Python 3.4 syntax and standard libraries
I downloaded Python 3.4.10 and, in my code, changed:
paths.Add(#"C:\Python38\Lib");
To:
paths.Add(#"C:\Python-3.4.10\Lib");

C# app to call Python script that uses USB-to-CAN transceiver

Just wondering if someone out there might have some insight to what's going wrong here...
I have a python script that connects to a USB-to-CAN transceiver/dongle (made by PEAK System) to do some CAN communications. The script works pretty flawlessly. The script accepts command-line arguments and works fine when called from the Windows command-line.
I am trying to integrate this script into a C# Forms project. I have been successful at calling the Python script from the C# app, but things fall apart when it gets to the point at which the Python script tries to use the CAN transceiver.
It feels like the C# app front-end is not allowing the Python script to access the serial port.
Here is the error I get (Python script writing to StandardOut on the Visual Studio output):
line 86, in canSendRec
self.bus.send(canMessage, timeout=0.1)
AttributeError: 'Node' object has no attribute 'bus'
Unable to Connect to USB-CAN Device
Here is the line from canSendRec -- where the exception handler came from (which we wrote):
try:
self.bus = can.interface.Bus('PCAN_USBBUS1',bitrate=1000000)
self.bus.flush_tx_buffer()
except:
print("Unable to Connect to USB-CAN Device")
Here is my C# code calling the Python script:
public string pythonMakeCall(string script, string arg1){
ProcessStartInfo pyProcessStartInfo = new ProcessStartInfo(py_path);
pyProcessStartInfo.FileName = py_path;
pyProcessStartInfo.Arguments = string.Format("{0} {1}", script, arg1);
pyProcessStartInfo.CreateNoWindow = true;
pyProcessStartInfo.UseShellExecute = false;
pyProcessStartInfo.RedirectStandardOutput = true;
pyProcessStartInfo.RedirectStandardError = true;
Process pyProcess = new Process();
pyProcess.StartInfo = pyProcessStartInfo;
pyProcess.Start();
retString = pyProcess.StandardOutput.ReadToEnd();
pyProcess.WaitForExit();
return retString;}
Like I said, it feels like there is something going on with the C# app not allowing Python to access the USB ports, but I'm not sure where to begin with debugging that hunch, since, Disclaimer: this is my first time dabbling in C#/Visual Studio and I'm no Python expert either.

Serialization of compiled code

A question I can't figure out, I am having fun with a little tool that I am making, the idea is that I allow the user that he/she writes his/her own C# code, and I save that code into a DB, once they wish to run that script I simply execute the following code:
var Script = CSharpScript.Create<string>(ScriptText, options, typeof(Script_Host));
var _Compilation = Script.Compile();
ScriptRunner<string> runner = Script.CreateDelegate();
Script_Host globals = new Script_Host();
globals._Parent = this;
globals._ConnectionString = _ConnectionString;
globals.Source = Source;
globals.Destination = Destination;
globals.Parameters = Parameters;
globals.Result = "";
_ScriptResult = runner(globals).Result;
Result = _ScriptResult;
So I have compiled the code and it's runnable and it works, and that is great, but it has a flaw. It means every time a user wants to run the code I have to take that piece of code, compile it and run it ... this takes time.
Now I simply want to take that compiled code, serialize it and insert it into a DB as varbinary ... so each script gets compiled only once (or more if they are doing an update) and that's it..
What is best way to reach for the compiled script, how can I convert it to varbinary?
You're using the wrong tool for the job.
Scripting is about "quickly executing a string of code" if you want to dynamically make assemblies and keep them around a standard compilation process is the better way to go ...
Executing a simple script:
await CSharpScript.EvaluateAsync("Console.WriteLine(\"Hello world!\")");
Creating assemblies:
https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/generating-and-compiling-source-code-from-a-codedom-graph
... the result here is that you end up with a physical assembly you can reuse not a context based on the fly generated blob of script in ram.

How do I call a specific Method from a Python Script in C#?

I'm wondering if there is a possibility to call a specific Method from a Python script over a C# project.
I have no code... but my idea is:
Python Code:
def SetHostInfos(Host,IP,Password):
Work to do...
def CalcAdd(Numb1,Numb2):
Work to do...
C# Code:
SetHostInfos("test","0.0.0.0","PWD")
result = CalcAdd(12,13)
How can I call one of the Methods, from this Python script, over C#?
You can host IronPython, execute the script and access the functions defined within the script through the created scope.
The following sample shows the basic concept and two ways of using the function from C#.
var pySrc =
#"def CalcAdd(Numb1, Numb2):
return Numb1 + Numb2";
// host python and execute script
var engine = IronPython.Hosting.Python.CreateEngine();
var scope = engine.CreateScope();
engine.Execute(pySrc, scope);
// get function and dynamically invoke
var calcAdd = scope.GetVariable("CalcAdd");
var result = calcAdd(34, 8); // returns 42 (Int32)
// get function with a strongly typed signature
var calcAddTyped = scope.GetVariable<Func<decimal, decimal, decimal>>("CalcAdd");
var resultTyped = calcAddTyped(5, 7); // returns 12m
I found a similar way to do it, the call of the method is much easier with it.
C# Code goes as follows:
IDictionary<string, object> options = new Dictionary<string, object>();
options["Arguments"] = new [] {"C:\Program Files (x86)\IronPython 2.7\Lib", "bar"};
var ipy = Python.CreateRuntime(options);
dynamic Python_File = ipy.UseFile("test.py");
Python_File.MethodCall("test");
So basically I submit the Dictionary with the Library path which I want to define in my python file.
So the PYthon Script looks as follows:
#!/usr/bin/python
import sys
path = sys.argv[0] #1 argument given is a string for the path
sys.path.append(path)
import httplib
import urllib
import string
def MethodCall(OutputString):
print Outputstring
So The method call is now much easier from C#
And the argument passing stays the same.
Also with this code you are able to get a custom library folder
for the Python file which is very nice if you work in a network
with a lot of different PC's
You could make your python program take arguments on the command line then call it as a command line app from your C# code.
If that's the way to go then there are plenty of resources:
How do I run a Python script from C#?
http://blogs.msdn.com/b/charlie/archive/2009/10/25/hosting-ironpython-in-a-c-4-0-program.aspx

Embedding Perl Interpreter

just downloaded ActivePerl. I want to embed the perl interpreter in a C# application (or at least call the perl interpreter from C#). I need to be able to send send out data to Perl from C#, then receive the output back into C#.
I just installed ActivePerl, and added MS Script Control 1.0 as a reference. I found this code on the internet, but am having trouble getting it to work.
MSScriptControl.ScriptControlClass Interpreter = new MSScriptControl.ScriptControlClass();
Interpreter.Language = #"ActivePerl";
string Program = #"reverse 'abcde'";
string Results = (string)Interpreter.Eval(Program);
return Results;
Originally, it had 'PerlScript' instead of 'ActivePerl', but neither work for me. I'm not entirely sure what Interpreter.Language expects. Does it require the path to the interpreter?
Solved... I'm not sure how, but when I changed it back to PerlScript it works now. Still, I would like to know if MSScript Control is using ActivePerl or another interpreter.
You can run an external program as Maxwell suggests, in which case the external program can be Perl or anything else. It might be easier to use temp files to send the input data and get the output, but that depends on how the external program expects to get its data.
The alternative, which is what I think you're looking for, is to use the PerlNET compiler that comes with ActiveState's Perl Dev Kit. It lets you add a class wrapper around the Perl code so you can expose it to C# just like any C# class. It's fairly simple to use; you add POD comments to your Perl code to specify the method names and signatures to expose, including type information, then you compile your Perl module into a DLL .NET assembly. Once that's done you can reference the assembly from any .NET program, construct an object from your Perl class, and call its methods.
I am not sure about the script control but I have done a similar thing where I had to 'embed' spamassasin (which is a Perl program). I basically used the Process to do the job. Something along the lines of:
var proc = new Process
{
StartInfo =
{
FileName = "perl",
WorkingDirectory = HttpRuntime.AppDomainAppPath,
Arguments = " myscript.pl arg1 arg2",
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false
}
};
proc.Start();
proc.StandardInput.BaseStream.Write... // feed STDIN
proc.StandardOutput.Read... // Read program output
var procStdErr = proc.StandardError.ReadToEnd(); // errors
proc.StandardError.Close();
proc.StandardOutput.Close();
proc.WaitForExit(3000);
int exitCode = proc.ExitCode;
proc.Close();
This obviously not just Perl specific and it has the process creation overhead, so if you are running your script too often probably you need to think of a different solution.

Categories

Resources