Accessing .Net enums in Iron python - c#

I'm trying to access .Net(C#) enums in IronPython, lets say we have
Test.dll
// Contains Several Enums
enum TestType{..}
enum TestQuality{..}
....
....
enum TestStatus{..}
//Similarly Multiple functions
public void StartTest(TestType testType, TestQuality testQuality){..}
....
....
public TestStatus GetTestStatus(){..}
and now if I try to call the above functions, I need to choose the appropriate enum parameters and so far what I did is this,
Iron Python [vs2012]
import clr
clr.AddReference('Test.dll')
from TestDll import *
test = Test()
# Initiate Enums
enumTestType = TestType
enumTestQuality = TestQuality
....
....
enumTestStatus = TestStatus
#Call Functions
test.StartTest(enumTestType.Basic, enumTestQuality.High)
....
....
# goes on
now the above IronPython code works fine, the only odd bit here is that I need to initiate all the enums(Intellisence doesnt work here) before I use them with the functions, this will become more difficult when there are more enums to use. whereas in C# environment(vs2012) we dont have to initiate but we can use them straight away when calling functions.
Is there a better way of dealing this in IronPython?
Please correct me if I'm wrong, thanks!

Assuming the enums are contained within your Test class you can either use them fully qualified
test.StartTest(Test.TestType.Basic, Test.TestQuality.High)
or by importing
from TestDll.Test import TestQuality, TestType
test.StartTest(TestType.Basic, TestQuality.High)
If the enums are in the same namespace as the Test class they should be usable without additional imports:
test.StartTest(TestType.Basic, TestQuality.High)

I had the same problem, but I fixed it in another way: using ScriptRuntime.LoadAssembly.
Prerequisites:
VS2013
C# app executable, plus the Test.dll assembly. IronPython is hosted by the C# app.
Test.dll: (note that all is within the TestDll namespace)
namespace TestDll
{
// Contains Several Enums
enum TestType{..}
enum TestQuality{..}
....
....
enum TestStatus{..}
//Similarly Multiple functions
public void StartTest(TestType testType, TestQuality testQuality){..}
....
....
public TestStatus GetTestStatus(){..}
}
I simply created the IronPython engine this way:
eng = Python.CreateEngine();
eng.Runtime.LoadAssembly(Assembly.GetAssembly(typeof(TestType))); // This allows "from TestDLL import *" in Python scripts
and then, execute the script with the usual
string pysrc = ...; // omitted, taken from the python script below
ScriptSource source = eng.CreateScriptSourceFromString(pysrc);
ScriptScope scope = eng.CreateScope();
source.Execute(scope);
This allowed me to write this Python code and execute it within the C# app: (note that I'm using the enum names directly)
from TestDll import *
test = Test()
#Call Functions
test.StartTest(TestType.Basic, TestQuality.High)
....
....
# goes on

Related

How to code PowerShell script and C# code in the same script

I know it is possible to call C# code from the PowerShell script by loading an assembly. But is there any way to pass and receive a value in between both C# code and PowerShell script.
Let's say I have a $path variable in my power script. I want to pass it to my c# code. And C# code will use the $path. After doing some stuff in the c# code it will return some value to the script. Is this possible? If it is, how can I do it? I must load a third party dll in my power shell and all one or two public methods on that dll to complete some task.
My PowerShell script code:
$scriptpath = $MyInvocation.MyCommand.Path;
$cureentDir = Split-Path $scriptpath;
$isSasDir = $cureentDir -match "mydir";
$requiredFile = "Core.dll";
$myPowersehllVal = "has value for c# code";
My C# code:
$Source = #"
using System.Net;
public class ExtendedWebClient : WebClient
{
String myPowersehllVal;
public int Timeout;
protected override WebRequest GetWebRequest(System.Uri address)
{
}
}
"#;
For getting PS values into C#
https://stackoverflow.com/a/22384009/3546415
In a more general sense, System.Management.Automation (Nuget Required) looks promising, in particular, Runspace and Pipeline.
Here are some good examples of usage:
https://msdn.microsoft.com/en-us/library/ee706576(v=vs.85).aspx
Something like this one seems similar to what you want.
Or maybe just use the PowerShell Class to execute PS commands from your C# module to set PS variable values.
Alternatively, without worrying about interop, you can kind of hack this by working through the file system and/or environmental variables. Following this paradigm, you could even use a memory mapped file and share variables with a broader set of applications. Powershell side would be something like this. For objects, serialization.

C# creating a wrapper namespace?

In C#, how can I import all classes from one namespace into another namespace such that these classes are directly accessible from the second namespace?
I'm essentially attempting to rename a namespace in an externally visible manner.
Since code is worth a thousand words, given a DLL with the following namespace:
// Externally written DLL I have no control over.
namespace A
{
class ClassA {...}
}
I'd like to be able to create another DLL along the lines of:
// My DLL
namespace Wrapper
{
using A;
}
So that I can use it like:
// Final C# program.
using Wrapper;
var a = ClassA();
In python, I could accomplish what I want with import *:
# external.py
class ClassA:
...
# mymodule.py
from external import *
# final_program.py
import mymodule
a = mymodule.ClassA()
Disclaimer
I know this is a terrible idea, but I'm unfortunately being constrained by external requirements. The short version is that I have an external DLL that needs to interface with a proprietary system (EnScript, if you're curious). This proprietary system has restrictions on the naming of namespaces that the external DLL of course violates. Thus, I'm attempting to use the wrapper DLL to expose a namespace that is considered valid.
Related Questions
Talks about using in C# vs wildcard imports in java/python. Does not address issue of accessing from second namespace:
Import all subclasses like Java but in C#
C# equivalent to wildcard imports in Java
Namespaces in C# vs imports in Java and Python
Question about including classes in namespace. Issue was use of separate projects and so not applicable to this question:
How To Include Classes From Another Namespace In Assembly Instead of Writing Them Into A Separate DLL File?
You can't move a type to a different namespace (other than physically moving the code). The .NET type system uses the full namespace to uniquely identify the type.
But you can create an alias to mask the original namespace.
Let's say you have a class MyProject.Foo.Bar.Xyzzy.MyClass, and you are tired of typing MyProject.Foo.bar.Xyzzy. You can add a Using directive at the top of the code file like this:
Using DifferentNamespace = MyProject.Foo.Bar.Xyzzy;
Once you have done this, you can refer to the class with just
var c = new DifferentNamespace.MyClass();
You can even use this to include a different namespace in the current default namespace. This will compile:
namespace Example.Classes
{
class MyClass
{
}
}
namespace Example
{
using Example = Example.Classes;
class Test
{
static void Test1()
{
var c = new Example.MyClass(); //Not Example.Classes.MyClass
}
}
}
But unfortunately you have to leave the alias there; i.e., this won't compile:
namespace Example.Classes
{
class MyClass
{
}
}
namespace Example
{
using Example = Example.Classes;
class Test
{
static void Test1()
{
var c = new MyClass(); //Error
}
}
}

How to invoke C#/.NET namespace in IronPython?

I'm looking to replicate the following in IronPython and searching has so far been fruitless and/or disappointing.
namespace Groceries
{
public class ChocolateMilk : Milk
{
// Other stuff here
}
}
The idea would be that the compiled Python DLL will be loaded into a C# program through System.Reflection.Assembly.Load and a GetType("Groceries.ChocolateMilk") on the loaded DLL would not return null.
The most recent answer I was able to find was in 2008 and said that it was impossible without using the Hosting API - http://lists.ironpython.com/pipermail/users-ironpython.com/2008-October/008684.html.
Any suggestions on how to accomplish this would be greatly appreciated. Any conclusions that this is currently impossible to do via IronPython will also be appreciated, but less so.
I'm a bit confused on what you're asking here. Are you trying to instantiate that C# code in your IronPython modules? Or do you have the equivalent classes written in IronPython and you want to instantiate them in your C# code?
Based on the link you posted, I suppose you're going for the latter and have IronPython classes that you want instantiated in your C# code. The answer is, you cannot directly instantiate them. When you compile IronPython code to an assembly, you cannot use the types defined there with your regular .NET code since there is not a one-to-one mapping between IronPython classes and .NET classes. You would have to host the assembly in your C# project and instantiate it that way.
Consider this module, Groceries.py compiled to Groceries.dll residing in the working directory:
class Milk(object):
def __repr__(self):
return 'Milk()'
class ChocolateMilk(Milk):
def __repr__(self):
return 'ChocolateMilk()'
To host the module in your C# code:
using System;
using IronPython.Hosting;
using System.IO;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var engine = Python.CreateEngine();
var groceriesPath = Path.GetFullPath(#"Groceries.dll");
var groceriesAsm = Assembly.LoadFile(groceriesPath);
engine.Runtime.LoadAssembly(groceriesAsm);
dynamic groceries = engine.ImportModule("Groceries");
dynamic milk = groceries.ChocolateMilk();
Console.WriteLine(milk.__repr__()); // "ChocolateMilk()"
}
}
Otherwise to go the other way and create an instance of your .NET type in your IronPython code (as your title suggests). You'd need to add the path to your assembly, reference it, then you could instantiate it as needed.
# add to path
import sys
sys.path.append(r'C:\path\to\assembly\dir')
# reference the assembly
import clr
clr.AddReferenceToFile(r'Groceries.dll')
from Groceries import *
chocolate = ChocolateMilk()
print(chocolate)

Build Python scripts and call methods from C#

Is there any way to make this scenario work?
There is a Python script. It is built into a DLL by running this script with IronPython:
import clr
clr.CompileModules("CompiledScript.dll", "script.py")
The goal is to call this DLL's methods from C# code. .NET Reflector shows there is one class in the DLL - DLRCashedCode and the methods we are interested in are private static methods of this class.
For example, there is a function in the script:
def scriptMethod(self, text):
...
Its representation in the DLL is:
private static object scriptMethod(Closure closure1, PythonFunction $function, object self, object text)
{
...
}
Closure and PythonFunction are IronPython classes (from Microsoft.Scripting.dll and IronPython.dll).
So far so good. Is it possible this method to be called by C# code? The idea of using reflection like
Type t = typeof(DLRCachedCode);
string methodName = "scriptMethod";
MethodInfo method = t.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
object[] parameters = new object[] { "param1", "param2" }; // the "params problem"
method.Invoke(null, parameters);
seems harder because of setting the method's parameters. If they are (any how) initialized correctly, could we expect the method to work smoothly?
Is there a better way to call this methods from C#? For various different reasons we prefer to have the script built as a .NET assembly and not to call the script itself.
Sort of. You cannot access the Python methods directly from C# code. Unless you are playing with C# 4.0 and the dynamic keyword or you are very, very special ;). However, you can compile an IronPython class to a DLL and then use IronPython hosting in C# to access the methods (this is for IronPython 2.6 and .NET 2.0).
Create a C# program like this:
using System;
using System.IO;
using System.Reflection;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
// we get access to Action and Func on .Net 2.0 through Microsoft.Scripting.Utils
using Microsoft.Scripting.Utils;
namespace TestCallIronPython
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
ScriptEngine pyEngine = Python.CreateEngine();
Assembly myclass = Assembly.LoadFile(Path.GetFullPath("MyClass.dll"));
pyEngine.Runtime.LoadAssembly(myclass);
ScriptScope pyScope = pyEngine.Runtime.ImportModule("MyClass");
// Get the Python Class
object MyClass = pyEngine.Operations.Invoke(pyScope.GetVariable("MyClass"));
// Invoke a method of the class
pyEngine.Operations.InvokeMember(MyClass, "somemethod", new object[0]);
// create a callable function to 'somemethod'
Action SomeMethod2 = pyEngine.Operations.GetMember<Action>(MyClass, "somemethod");
SomeMethod2();
// create a callable function to 'isodd'
Func<int, bool> IsOdd = pyEngine.Operations.GetMember<Func<int, bool>>(MyClass, "isodd");
Console.WriteLine(IsOdd(1).ToString());
Console.WriteLine(IsOdd(2).ToString());
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
Make a trivial Python class like this:
class MyClass:
def __init__(self):
print "I'm in a compiled class (I hope)"
def somemethod(self):
print "in some method"
def isodd(self, n):
return 1 == n % 2
Compile it (I use SharpDevelop) but the clr.CompileModules method should also work. Then shove the compiled MyClass.dll into the directory where the compiled C# program lives and run it. You should get this as the result:
Hello World!
I'm in a compiled class (I hope)
in some method
in some method
True
False
Press any key to continue . . .
This incorporates Jeff's more direct solution that eliminates having to create and compile a small Python 'stub' and also shows how you can create C# function calls that access the methods in the Python class.
The clr.CompileModules is purely a load-time optimization - it doesn't make the scripts directly available to a static languge like C#. You'll need to host the IronPython runtime, and then you can load the DLL into the runtime and use IronPython's hosting interfaces to access it.

Instantiating a python class in C#

I've written a class in python that I want to wrap into a .net assembly via IronPython and instantiate in a C# application. I've migrated the class to IronPython, created a library assembly and referenced it. Now, how do I actually get an instance of that class?
The class looks (partially) like this:
class PokerCard:
"A card for playing poker, immutable and unique."
def __init__(self, cardName):
The test stub I wrote in C# is:
using System;
namespace pokerapp
{
class Program
{
static void Main(string[] args)
{
var card = new PokerCard(); // I also tried new PokerCard("Ah")
Console.WriteLine(card.ToString());
Console.ReadLine();
}
}
}
What do I have to do in order to instantiate this class in C#?
IronPython classes are not .NET classes. They are instances of IronPython.Runtime.Types.PythonType which is the Python metaclass. This is because Python classes are dynamic and support addition and removal of methods at runtime, things you cannot do with .NET classes.
To use Python classes in C# you will need to use the ObjectOperations class. This class allows you to operate on python types and instances in the semantics of the language itself. e.g. it uses the magic methods when appropriate, auto-promotes integers to longs etc. You can find out more about ObjectOperations by looking at the source or using reflector.
Here is an example. Calculator.py contains a simple class:
class Calculator(object):
def add(self, a, b):
return a + b
You can use it from your pre .NET 4.0 C# code like this:
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
ObjectOperations op = engine.Operations;
source.Execute(scope); // class object created
object klaz = scope.GetVariable("Calculator"); // get the class object
object instance = op.Call(klaz); // create the instance
object method = op.GetMember(instance, "add"); // get a method
int result = (int)op.Call(method, 4, 5); // call method and get result (9)
You will need to reference the assemblies IronPython.dll, Microsoft.Scripting and Microsoft.Scripting.Core.
C# 4 made this much easier with the new dynamic type.
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);
dynamic Calculator = scope.GetVariable("Calculator");
dynamic calc = Calculator();
int result = calc.add(4, 5);
If you are using Visual Studio 2010 or later with NuGet support simply execute this to download and reference the appropriate libraries.
Install-Package IronPython
Now that .Net 4.0 is released and has the dynamic type, this example should be updated. Using the same python file as in m-sharp's original answer:
class Calculator(object):
def add(self, a, b):
return a + b
Here is how you would call it using .Net 4.0:
string scriptPath = "Calculator.py";
ScriptEngine engine = Python.CreateEngine();
engine.SetSearchPaths(new string[] {"Path to your lib's here. EG:", "C:\\Program Files (x86)\\IronPython 2.7.1\\Lib"});
ScriptSource source = engine.CreateScriptSourceFromFile(scriptPath);
ScriptScope scope = engine.CreateScope();
ObjectOperations op = engine.Operations;
source.Execute(scope);
dynamic Calculator = scope.GetVariable("Calculator");
dynamic calc = Calculator();
return calc.add(x,y);
Again, you need to add references to IronPython.dll and Microsoft.Scripting.
As you can see, the initial setting up and creating of the source file is the same.
But once the source is succesfully executed, working with the python functions is far easier thanks to the new "dynamic" keyword.
I am updating the above example provided by Clever Human for compiled IronPython classes (dll) instead of IronPython source code in a .py file.
# Compile IronPython calculator class to a dll
clr.CompileModules("calculator.dll", "calculator.py")
C# 4.0 code with the new dynamic type is as follows:
// IRONPYTHONPATH environment variable is not required. Core ironpython dll paths should be part of operating system path.
ScriptEngine pyEngine = Python.CreateEngine();
Assembly myclass = Assembly.LoadFile(Path.GetFullPath("calculator.dll"));
pyEngine.Runtime.LoadAssembly(myclass);
ScriptScope pyScope = pyEngine.Runtime.ImportModule("calculator");
dynamic Calculator = pyScope.GetVariable("Calculator");
dynamic calc = Calculator();
int result = calc.add(4, 5);
References:
Using Compiled Python Classes from .NET/CSharp IP 2.6
Static Compilation of IronPython scripts
I have searched high and low and I am afraid that there does not seem to be much information pertaining to this. I am pretty much certain that no one has devised a way to do this in the clean manner that you would like.
The main reason I think this is a problem is that in order to see the PokerCard type in your C# application you would have to compile your Python code to IL. I don't believe that there are any Python->IL compilers out there.

Categories

Resources