Passing command line parameters to IronPython from C# application? - c#

How do I pass command line parameters from my C# application to IronPython 2.x? Google is only returning results about how to do it with Iron Python 1.x.
static void Main(string[] args)
{
ScriptRuntime scriptRuntime = IronPython.Hosting.Python.CreateRuntime();
// Pass in script file to execute but how to pass in other arguments in args?
ScriptScope scope = scriptRuntime.ExecuteFile(args[0]);
}

You can either set the sys.argv via the following C# code:
static void Main(string[] args)
{
var scriptRuntime = Python.CreateRuntime();
var argv = new List();
args.ToList().ForEach(a => argv.Add(a));
scriptRuntime.GetSysModule().SetVariable("argv", argv);
scriptRuntime.ExecuteFile(args[0]);
}
having the following python script
import sys
for arg in sys.argv:
print arg
and calling the exe like
Test.exe SomeScript.py foo bar
gives you the output
SomeScript.py
foo
bar
Another option would be passing the prepared options to Python.CreateRuntime as explained in this answer

Related

How to properly setup CodeContext of IronPython to directly invoke IO from C#?

I am trying to directly invoke IronPython's built-in modules from C#. It looks like I'm missing some important initialization, that I can't find anywhere in the code.
Here's what I do:
namespace py.consoleio
{
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Hosting.Providers;
using Microsoft.Scripting.Runtime;
public static class consoleio
{
public static string name;
static void Main()
{
var setup = new ScriptRuntimeSetup();
setup.LanguageSetups.Add(
IronPython.Hosting.Python.CreateLanguageSetup(null));
var dlrRuntime = new ScriptRuntime(setup);
var scriptDomainManager = HostingHelpers.GetDomainManager(dlrRuntime);
var pythonContext = new PythonContext(scriptDomainManager, null);
var context = new CodeContext(new PythonDictionary(), new ModuleContext(new PythonDictionary(), DefaultContext.DefaultPythonContext));
name = IronPython.Modules.Builtin.input(context, "What is your name?\n");
IronPython.Modules.Builtin.print(context, "Hi, %s.", consoleio.name);
System.GC.KeepAlive(pythonContext);
}
}
}
That properly outputs "What is your name?", but then crashes trying to decode input: unknown encoding: cp437.
Now I've already found, that encodings are initialized in Src/StdLib/Lib/encodings/init.py
I can't find how it gets to loading this module in a normal IronPython run (e.g. a console host), so I can't reproduce it in C# program.
My goal here is to invoke IronPython functions without dynamic dispatch.
UPD. Now I also tried to do this:
var engine = Python.CreateEngine();
this.ScriptDomainManager = HostingHelpers.GetDomainManager(engine.Runtime);
to the same result
Figured that one out: encodings module is implemented in Python in IronPython (core modules are in C#). It always worked with IronPythonConsole project, because it implicitly adds IronPython source for standard libraries to Python path. I just had to explicitly specify path like this:
var options = new Dictionary<string, object> { ["SearchPaths"] = path };
var engine = Python.CreateEngine(options);

Roslyn Run Code in AppDomain

I added Roslyn my project.
Roslyn can run script from string like
using Roslyn.Scripting.CSharp;
namespace RoslynScriptingDemo
{
class Program
{
static void Main(string[] args)
{
var engine = new ScriptEngine();
engine.Execute(#"System.Console.WriteLine(""Hello Roslyn"");");
}
}
}
but I wanna access the controls,properties,variables in the form.
For Example There is a textbox in form.
var engine = new ScriptEngine();
engine.Execute(#" textbox1.Text="SK"; ");
Can I access controls in Roslyn?
You could set up a host object for your script session that has a public property for the controls you want to access.

how to pass argument to a process

Is there a way to pass a string argument to a process which is spawned from my own process.
I have in my main application:
Process.Start(Path.Combine(Application.StartupPath, "wow.exe");
wow.exe is another app I created. I need to pass argument to this exe (a string). How can I achieve this typically?
What I tried:
ProcessStartInfo i = new //........
i.Argument = "cool string";
i. FileName = Path.Combine(Application.StartupPath, "wow.exe");
Process.Start(i);
And in the main of wow application i wrote:
static void Main()
{
//print Process.GetCurrentProcess().StartInfo.Argument;
}
But I never get my string there in second application's Main. Here is a question which asks why, but no how to solve it..
Edit: Environment.GetCommandLineArgs()[1], it has to be. Nevertheless, got it working. Accepted #Bali's answer as he cameup first with this answer. Thanks all
To get the arguments passed you can either use the string[] args in your Main, or you can use Environment.GetCommandLineArgs.
Example:
Console.WriteLine(args[0]);
or
Console.WriteLine(Environment.GetCommandLineArgs[0]);
You probably want a
static void Main(string[] args)
{
}
where args contains the arguments you passed in
Here's an example how you can get arguments passed to your exe:
static void Main()
{
string[] args = Environment.GetCommandLineArgs();
string firstArgument = args[0];
string secondArgument = args[1];
}
or change your main method a bit:
static void Main(string []args)
{}
In your wow.exe program.cs
static void Main()
{
//Three Lines of code
}
change it to
static void Main(string[] args)
{
//Three Lines of code
}
string[] args. will now contain your arguments passed to your exe.
Or you can use
string[] arguments = Environment.GetCommandLineArgs();
Your arguments are broken by space " ".

Calling a function with named arguments in a hosted application

So I am hosting IronPython in my C# application. IronPhyton is used to implement a DSL for users. The DSL syntax should be something like this:
Ping(Message = "testOne1")
The hosting code looks like:
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
Action<string> ping = (message) => Console.WriteLine(message.ToString());
scope.SetVariable("Ping", ping);
var script = #"
Ping(Message = ""testOne1"")
";
engine.Execute(script, scope);
But this does not work because Action<string> does not keep name of the argument. Calling it without the parameter name works as expected:
Ping("testOne1")
How do I store a function and call it with named arguments?
To use named arguments you'll have to define the method statically. For example, I'll just put all DSL operations into an Operations static class.
public static class Operations {
public static void Ping(string Message) {
Console.WriteLine(Message);
}
}
Then named arguments will work:
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
// Load the assembly where the operations are defined.
engine.Runtime.LoadAssembly(Assembly.GetExecutingAssembly());
// Import the operations modules, settings their names as desired.
engine.Execute(#"
from Operations import Ping
", scope);
// Now named arguments will work...
var script = #"
Ping(Message = ""Ping!"")
";
engine.Execute(script, scope);
Now if I could give you some advise; I'd prefer to implement the actual Python API in Python, and have that call back into my .NET code as needed. For example, instead of having the "operations" defined in C#, you'd have an Operations.py file which defines your Python DSL:
# Get access to your .NET API
import clr
clr.AddReference("MyAPI")
import MyAPI
# Define the Ping call to call into your .NET API
def Ping(Message):
MyAPI.Ping(Message)
And your hosting code doesn't need to change at all.
Both are valid solutions, but the last one lets you iterate on your DSL easily.
Good luck!
The name of the parameter is defined by the name provided in the delegate type. In the case of Action<T>, the parameter name is obj.
public delegate void Action<in T>(
T obj
)
obj should work for you. Are you sure it isn't working? It works for me.
In an IronPython project I have a library:
namespace TestLibrary
{
public static class Test
{
public static readonly Action<string> WriteLine =
msg => Console.WriteLine(msg);
// the same works if I do this instead
//public static readonly Action<string> WriteLine = Console.WriteLine;
}
}
And this works:
from TestLibrary import Test
#Test.WriteLine(msg='foo') # error
Test.WriteLine(obj='foo') # works
Hosted, same deal:
var engine = Python.CreateEngine();
dynamic scope = engine.CreateScope();
Action<string> writeLine = msg => Console.WriteLine(msg);
// or even
//Action<string> writeLine = Console.WriteLine;
scope.writeLine = writeLine;
//engine.Execute("writeLine(msg='foo')", scope); // error
engine.Execute("writeLine(obj='foo')", scope); // works

C#, IronPython - import(?) from a non-static class

I have a non-static C# class with some instance methods, which I need to call from IronPython scripts. Currently I'm doing it this way:
scope.SetVariable("class", instanceOfClass);
in C# code and
class.SomeMethod(args)
in script.
What I want is being able to call this class methods without adding class. each time in the script. Each script has its own instance of the class, and only one instance is used in one script.
If this class was static, the solution would be from ClassName import *, but as I know there is no similar construction for non-static classes.
How can this be done? I have some ideas (such as using reflection, or adding class. to each call in Python source programmatically), but they are overcomplicated and may be even not possible to implement.
UPD:
Problem solved by using such python code (before actual script):
def Method1(arg1): # for simple method
class.Method1(arg1)
def Method2(arg = 123): # for default values
class.Method2(arg)
def Method3(*args): # for params
class.Method3(args)
# so on
from ClassName import * is actually from namespace import type. This statement makes the type avaiable for use via the type name in Python. It makes no difference if the class is static or not. Consider this sample code - Environment being the static class.
import clr
from System import Environment
print Environment.CurrentDirectory
To solve your problem, inject a delegate to the class function into your ScriptScope, rather than the class itself.
Sample class
public class Foo {
public string GetMyString(string input) {
return input;
}
}
Usage
private static void Main(string[] args) {
ScriptEngine engine = Python.CreateEngine();
string script = "x = GetMyString('value')";
Foo foo = new Foo();
ScriptSource scriptSource = engine.CreateScriptSourceFromString(script);
ScriptScope scope = engine.CreateScope();
scope.SetVariable("GetMyString", new Func<string, string>(foo.GetMyString));
scriptSource.Execute(scope);
string output = scope.GetVariable<string>("x");
Console.WriteLine(output);
}
prints
value

Categories

Resources