C# and IronPython integration - c#

I want simply use io.py from C# to write a file and I use the following code:
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
...
System.IO.Directory.SetCurrentDirectory(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) +
"\IronPython\Lib");
ScriptRuntime py = Python.CreateRuntime();
dynamic io = py.UseFile("io.py");
dynamic f = io.open("tmp.txt", "w");
f.writelines("some text...");
f.close();
but when I run the program the runtime give me a:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException telling that no overload of writelines accept argument '1'
it seems like that method doesn't exists... but into io.py documentation it exists.
P.S. The same is for close method!!!
Any idea?

I can only tell you how to make your code working, however I don't have much experience with IronPython and have no idea why it is done this way (though I try to learn that). First, it seems io module is treated in a special way and there is special (non-dynamic) class for that. When you do io.open what is returned is instance of PythonIOModule._IOBase class. You can do
var f = (PythonIOModule._IOBase) io.open("tmp.txt", "w");
And see for yourself that "writeline" method (which is regular method, not a dynamic one) accepts CodeContext instance as first argument, and second argument is lines. Interesting that this class itself already contains field with that CodeContext, but it is made internal for some reason, and what is even worse - writelines (and other methods) could have been using that CodeContext and not require us to provide external one. Why is it done like this - I have no idea.
So to make your code working, we have to get CodeContext somewhere. One way is do that via reflection:
var context = (CodeContext) f.GetType().GetField("context", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(f);
Another way is to craft it yourself:
var languageContext = HostingHelpers.GetLanguageContext(engine);
var context = new ModuleContext(io._io, new PythonContext(languageContext.DomainManager, new Dictionary<string, object>())).GlobalContext;
Both methods will work and program will successfully write to a file. Full working sample:
static void Main(string[] args) {
System.IO.Directory.SetCurrentDirectory(#"G:\Python27\Lib");
var engine = Python.CreateEngine();
dynamic io = engine.ImportModule("io");
var f = (PythonIOModule._IOBase) io.open("tmp.txt", "w");
var languageContext = HostingHelpers.GetLanguageContext(engine);
var context = new ModuleContext(io._io, new PythonContext(languageContext.DomainManager, new Dictionary<string, object>())).GlobalContext;
f.writelines(context, "some text....");
f.close(context);
}

Related

Azure.Messaging.EventGrid vs Azure.EventGrid IotHubDeviceTelemetryEventData missing Constructor?

I am attempting to use the newer Azure.Messaging.EventGrid over the traditional Azure.EventGrid. I am getting hung up on my unit tests attempting to create object of type IotHubDeviceTelemetryEventData(). In the older library, I was able to create this no problem using the following convention.
return new object[]
{
new
{
id = "73813f6e-4d43-eb85-d6f1-f2b6a0657731",
topic = "testTopic",
data = new IotHubDeviceTelemetryEventData <-- New Up the object (no problem!)
{
Body = body} <-- Body has a setter. Great!
,
eventType = "Microsoft.Devices.DeviceTelemetry",
subject = "devices/b82bfa90fb/gw-uplink",
dataVersion = "1.0"
}
With the latest offering however, all of this is removed for some reason.
Old documentation with constructor etc (https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.eventgrid.models.iothubdevicetelemetryeventdata.-ctor?view=azure-dotnet
New documentation with no constructor, no setter on the body (DeviceTelemetry is sealed) etc:
https://learn.microsoft.com/en-us/dotnet/api/azure.messaging.eventgrid.systemevents.iothubdevicetelemetryeventdata?view=azure-dotnet
Anyone run into this? I would like to get off the old but I have existing unit tests that logically create TelemetryEventData and send to the function. I see no way of unit testing this ? I have tried mocking IotHubDeviceTelemetryEventData with
_mockHubTelemEventData.setup(c => c.Body).Returns(foo)
but this as well throws me an error of no setter on Body.
Super frustrating.
Other attempts have included creating EventGridEvent() but this as well is missing core functionality as the EventGridEvent.parse won't find any object of type Body.
EventGridEvent[] egEvents = EventGridEvent.ParseMany(BinaryData.FromStream(req.Body));
Mocking code you don't own comes with downsides, and you just one of them. But that's not why you're here. If you want to create instances of IotHubDeviceTelemetryEventData, you can try creating them as JSON and deserialize them. Give this a shot:
using System.Text.Json;
using Azure.Messaging.EventGrid.SystemEvents;
var json = #"
{
""body"":
{
""property"": { ""foo"": ""bar"" }
}
}
";
var eventData = JsonSerializer.Deserialize<IotHubDeviceTelemetryEventData>(json);

Roslyn add new method to an existing class

I'm investigating the use of the Roslyn compiler within a Visual Studio Extension (VSIX) that uses the VisualStudioWorkspace to update existing code. Having spent the last few days reading up on this, there seem to be several ways to achieve this....I'm just not sure which is the best approach for me.
Okay, so let's assume that the User has their solution open in Visual Studio 2015. They click on my Extension and (via a form) they tell me that they want to add the following method definition to an interface:
GetSomeDataResponse GetSomeData(GetSomeDataRequest request);
They also tell me the name of the interface, it's ITheInterface.
The interface already has some code in it:
namespace TheProjectName.Interfaces
{
using System;
public interface ITheInterface
{
/// <summary>
/// A lonely method.
/// </summary>
LonelyMethodResponse LonelyMethod(LonelyMethodRequest request);
}
}
Okay, so I can load the Interface Document using the following:
Document myInterface = this.Workspace.CurrentSolution?.Projects?
.FirstOrDefault(p
=> p.Name.Equals("TheProjectName"))
?.Documents?
.FirstOrDefault(d
=> d.Name.Equals("ITheInterface.cs"));
So, what is the best way to now add my new method to this existing interface, ideally writing in the XML comment (triple-slash comment) too? Bear in mind that the request and response types (GetSomeDataRequest and GetSomeDataResponse) may not actually exist yet. I'm very new to this, so if you can provide code examples then that would be terrific.
UPDATE
I decided that (probably) the best approach would be simply to inject in some text, rather than try to programmatically build up the method declaration.
I tried the following, but ended up with an exception that I don't comprehend:
SourceText sourceText = await myInterface.GetTextAsync();
string text = sourceText.ToString();
var sb = new StringBuilder();
// I want to all the text up to and including the last
// method, but without the closing "}" for the interface and the namespace
sb.Append(text.Substring(0, text.LastIndexOf("}", text.LastIndexOf("}") - 1)));
// Now add my method and close the interface and namespace.
sb.AppendLine("GetSomeDataResponse GetSomeData(GetSomeDataRequest request);");
sb.AppendLine("}");
sb.AppendLine("}");
Inspecting this, it's all good (my real code adds formatting and XML comments, but removed that for clarity).
So, knowing that these are immutable, I tried to save it as follows:
var updatedSourceText = SourceText.From(sb.ToString());
var newInterfaceDocument = myInterface.WithText(updatedSourceText);
var newProject = newInterfaceDocument.Project;
var newSolution = newProject.Solution;
this.Workspace.TryApplyChanges(newSolution);
But this created the following exception:
bufferAdapter is not a VsTextDocData
at Microsoft.VisualStudio.Editor.Implementation.VsEditorAdaptersFactoryService.GetAdapter(IVsTextBuffer bufferAdapter)
at Microsoft.VisualStudio.Editor.Implementation.VsEditorAdaptersFactoryService.GetDocumentBuffer(IVsTextBuffer bufferAdapter)
at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.InvisibleEditor..ctor(IServiceProvider serviceProvider, String filePath, Boolean needsSave, Boolean needsUndoDisabled)
at Microsoft.VisualStudio.LanguageServices.RoslynVisualStudioWorkspace.OpenInvisibleEditor(IVisualStudioHostDocument hostDocument)
at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.DocumentProvider.StandardTextDocument.UpdateText(SourceText newText)
at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioWorkspaceImpl.ApplyDocumentTextChanged(DocumentId documentId, SourceText newText)
at Microsoft.CodeAnalysis.Workspace.ApplyProjectChanges(ProjectChanges projectChanges)
at Microsoft.CodeAnalysis.Workspace.TryApplyChanges(Solution newSolution)
at Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioWorkspaceImpl.TryApplyChanges(Solution newSolution)
If I were you I would take advantage of all Roslyn benefits, i.e. I would work with the SyntaxTree of the Document rather than processing the files text (you are able to do the latter without using Roslyn at all).
For instance:
...
SyntaxNode root = await document.GetSyntaxRootAsync().ConfigureAwait(false);
var interfaceDeclaration = root.DescendantNodes(node => node.IsKind(SyntaxKind.InterfaceDeclaration)).FirstOrDefault() as InterfaceDeclarationSyntax;
if (interfaceDeclaration == null) return;
var methodToInsert= GetMethodDeclarationSyntax(returnTypeName: "GetSomeDataResponse ",
methodName: "GetSomeData",
parameterTypes: new[] { "GetSomeDataRequest" },
paramterNames: new[] { "request" });
var newInterfaceDeclaration = interfaceDeclaration.AddMembers(methodToInsert);
var newRoot = root.ReplaceNode(interfaceDeclaration, newInterfaceDeclaration);
// this will format all nodes that have Formatter.Annotation
newRoot = Formatter.Format(newRoot, Formatter.Annotation, workspace);
workspace.TryApplyChanges(document.WithSyntaxRoot(newRoot).Project.Solution);
...
public MethodDeclarationSyntax GetMethodDeclarationSyntax(string returnTypeName, string methodName, string[] parameterTypes, string[] paramterNames)
{
var parameterList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(GetParametersList(parameterTypes, paramterNames)));
return SyntaxFactory.MethodDeclaration(attributeLists: SyntaxFactory.List<AttributeListSyntax>(),
modifiers: SyntaxFactory.TokenList(),
returnType: SyntaxFactory.ParseTypeName(returnTypeName),
explicitInterfaceSpecifier: null,
identifier: SyntaxFactory.Identifier(methodName),
typeParameterList: null,
parameterList: parameterList,
constraintClauses: SyntaxFactory.List<TypeParameterConstraintClauseSyntax>(),
body: null,
semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken))
// Annotate that this node should be formatted
.WithAdditionalAnnotations(Formatter.Annotation);
}
private IEnumerable<ParameterSyntax> GetParametersList(string[] parameterTypes, string[] paramterNames)
{
for (int i = 0; i < parameterTypes.Length; i++)
{
yield return SyntaxFactory.Parameter(attributeLists: SyntaxFactory.List<AttributeListSyntax>(),
modifiers: SyntaxFactory.TokenList(),
type: SyntaxFactory.ParseTypeName(parameterTypes[i]),
identifier: SyntaxFactory.Identifier(paramterNames[i]),
#default: null);
}
}
Note that this is pretty raw code, Roslyn API is extremely powerful when it comes to analyzing/processing the syntax tree, getting symbol information/references and so on. I would recommend you to look at this page and this page for reference.

instantiate python class into C# using the class name as string

This SO question provides code to create an instance of a python class in C#.
The following code forces to know the python function name in advance. However I need to specify the class name and the function name to be executed by strings.
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);
dynamic class_object = scope.GetVariable("Calculator");
dynamic class_instance = class_object();
int result = class_instance.add(4, 5); // I need to call the function by a string
Easiest way to do that is to install nuget package called Dynamitey. It was designed specifically to call dynamic methods (and doing other useful things) on dynamic objects. After you install it, just do:
static void Main(string[] args)
{
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);
dynamic class_object = scope.GetVariable("Calculator");
dynamic class_instance = class_object();
int result = Dynamic.InvokeMember(class_instance, "add", 4, 5);
}
If you want to know what it does under the hood - it uses the same code which is used by C# compiler for dynamic invocations. This is a long story but if you want to read about this, you can do it here for example.
You're looking for the Invoke and InvokeMember IronPython methods:
ScriptEngine engine = Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromFile("Calculator.py");
ScriptScope scope = engine.CreateScope();
source.Execute(scope);
object class_object = scope.GetVariable("Calculator");
object class_instance = engine.Operations.Invoke(class_object);
object[] args = new object[2];
args[0] = 4;
args[1] = 5;
int result = (int)engine.Operations.InvokeMember(class_instance, "add", args); // Method called by string
// "args" is optional for methods which don't require arguments.
I also changed the dynamic type to object, since you won't need it anymore for this code sample, but you are free to keep it, should you need to call some fixed-name methods.

Accessing WinForm Controls using C# dynamic code

I have WinForm called Form1 and there are Button1, MemoEdit1 and 2 TextBoxes named TextBox1 and TextBox2. At runtime user should be able write C# code in MemoEdit1 in order to manipulate the TextBox controls. F.e: at runtime user typed into MemoEdit1 simple code like: TextBox2.Text = "Hello" + TextBox1.Text;
So, when I click on Button1, I need to compile and execute the code.
Question may sound so simple as I am a newbie in compiling/executing code during runtime in C#.
Could you pls, help?
Thanks.
Take a look on this snippet
public class Evaluator
{
public void Eval(string Code)
{
Microsoft.CSharp.CSharpCodeProvider Provider = new Microsoft.CSharp.CSharpCodeProvider(); // Create an provider
System.CodeDom.Compiler.ICodeCompiler Compiler = Provider.CreateCompiler(); // Create An Compiler
System.CodeDom.Compiler.CompilerParameters Parameters = new System.CodeDom.Compiler.CompilerParameters(); // Create a parameters of the compiler
Parameters.GenerateInMemory = true; // It should generate the compiled assembly in the memory
System.CodeDom.Compiler.CompilerResults Results = Compiler.CompileAssemblyFromSource(Parameters, Code); //Compile it
///Now you just need to use reflection to call its methods
object SomeClass = Results.CompiledAssembly.CreateInstance("ClassName"); //Name of the class you want to create an instance
var Method = SomeClass.GetType().GetMethod("MethodName"); //Name of the Method you want to call
Method.Invoke(SomeClass, null); // change null for the argument it needs
}
}
if you want to just write code you will have to add the an class and a Method to wrap the user code and then you call it through the Invoke, you will probably have to reference your own assembly into this assembly

Create an "all-in-one" function using DS in C#

In my web app I use several asmx (Web Services) from the same provider, they have one for this, other for that, but all require a SOAP Header with Authentication.
It is simple to add the Authentication:
public static SoCredentialsHeader AttachCredentialHeader()
{
SoCredentialsHeader ch = new SoCredentialsHeader();
ch.AuthenticationType = SoAuthenticationType.CRM5;
ch.UserId = "myUsername";
ch.Secret = apUtilities.CalculateCredentialsSecret(
SoAuthenticationType.CRM5, apUtilities.GetDays(), "myUsername", "myPassword");
return ch;
}
The problem is this SoCredentialsHeader come (derivation) from ONE webservice and I need to add the same code to the others, like:
public static wsContact.SoCredentialsHeader AttachContactCredentialHeader()
{
wsContact.SoCredentialsHeader ch = new wsContact.SoCredentialsHeader();
ch.AuthenticationType = wsContact.SoAuthenticationType.CRM5;
ch.UserId = "myUsername";
ch.Secret = apUtilities.CalculateCredentialsSecret(
wsContact.SoAuthenticationType.CRM5, apUtilities.GetDays(), "myUsername", "myPassword");
return ch;
}
public static wsDiary.SoCredentialsHeader AttachDiaryCredentialHeader()
{
wsDiary.SoCredentialsHeader ch = new wsDiary.SoCredentialsHeader();
ch.AuthenticationType = wsDiary.SoAuthenticationType.CRM5;
ch.UserId = "myUsername";
ch.Secret = apUtilities.CalculateCredentialsSecret(
wsDiary.SoAuthenticationType.CRM5, apUtilities.GetDays(), "myUsername", "myPassword");
return ch;
}
Is there a way to implement a design pattern in order to use only one function but that suits all webServices?
sometimes I see the T letter, is this a case for that? if yes, how can I accomplish such feature?
P.S. I could pass an enum and use a switch to check the enum name and apply the correct Header but everytime I need to add a new WebService, I need to add the enum and the code, I'm searcging for an advanced technique for this.
Thank you.
Create a file called whatever.tt (the trick is the .tt extension) anywhere in your VS solution and paste the following code:
using System;
namespace Whatever
{
public static class Howdy
{
<#
string[] webServices = new string[] {"wsContact", "wsDiary"};
foreach (string wsName in webServices)
{
#>
public static <#=wsName#>.SoCredentialsHeader AttachContactCredentialHeader()
{
<#=wsName#>.SoCredentialsHeader ch = new <#=wsName#>.SoCredentialsHeader();
ch.AuthenticationType = <#=wsName#>.SoAuthenticationType.CRM5;
ch.UserId = "myUsername";
ch.Secret = apUtilities.CalculateCredentialsSecret(<#=wsName#>.SoAuthenticationType.CRM5,
apUtilities.GetDays(), "myUsername", "myPassword");
return ch;
}
}
<# } #>
}
Then watch as a whatever.cs magically appears with the desired code snippets.These are called T4 templates for code generation in VS.
You'll want to turn these into partial classes or extension methods or something. The above code will not function "as is" but you get the idea.
I don't know if this is something you wish to consider, as it definitely has it's down sides, but - as ultimately these ( the varoius SoCredentialsHeader classes) are all copies of the same class definition in different namespaces so with a little bit of refactoring you could simply have one class and one method.
Copy the SoCredentialsHeader class definition to a project of your own, add a reference to it and remove the class definition from all web service's proxies.
Add a using statement at the top of the proxy code file and it would not tell the difference.
Basically you told it to use the same class definition (yours) for all web services.
The obvious down side is that you have to repeat this exercise whenever you update and web service reference (and that it assumes all services keep using the same definition), but we've been doing this in a similar scenario and it worked quite well for us.
I would try using a generic method, then use reflection to set the properties:
public static T AttachDiaryCredentialHeader<T>() where T: class
{
T ch = new T();
Type objType = ch.GetType();
PropertyInfo userId = objType.GetProperty("UserId");
authType.SetValue(ch, "myUsername", null)
//And so on for the other properties...
return ch;
}
IMHO, This is somewhat hacky, I would keep them separate, unless like the previous post mentioned it, you're absolutely sure the definitions of these services will remain the same. One minor change to one of them will break this.

Categories