Instantiate a .Net class from IronPython without boilerplate - python-import

To use a class from the .Net host application in IronPython, you can do this:
import clr
clr.AddReference('MyApplication')
from MyApplication import MyClass
x = MyClass()
But how can I do it without the first 3 lines or perhaps somehow executing them in the host application before it runs the script?

In Microsoft.Scripting.Hosting (which is part of the dynamic language runtime used in IronPython) you have the concept of a ScriptScope on which you can execute statements or source scripts.
This allows you to execute the boilerplate on a scope before executing the actual script. The following sample shows the basic idea:
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
var boilerplateSourceText = #"import clr
clr.AddReference('MyApplication')
from MyApplication import MyClass
";
var boilerplateSource = engine.CreateScriptSourceFromString(boilerplateSourceText, SourceCodeKind.Statements);
boilerplateSource.Execute(scope);
var scriptSource = engine.CreateScriptSourceFromString("x = MyClass()", SourceCodeKind.Statements);
scriptSource.Execute(scope);

You can add classes to the scope just like variables with SetVariable. In VB.Net:
Dim engine As Microsoft.Scripting.Hosting.ScriptEngine = IronPython.Hosting.Python.CreateEngine()
Dim scope As Microsoft.Scripting.Hosting.ScriptScope = engine.CreateScope()
scope.SetVariable("MyClass", IronPython.Runtime.Types.DynamicHelpers.GetPythonTypeFromType(GetType(MyClass)))
Dim source As Microsoft.Scripting.Hosting.ScriptSource = engine.CreateScriptSourceFromFile(pathToScript)
source.Execute(scope)

Related

Using log4net in F# as a singleton separate class

I've seen a few posts about implementing log4net using C# and F# - but I am wanting to implement it as a singleton in a separate class ( so I can call from anywhere )
I am loosely following this post. I just think my translation from C# to F# is a bit behind.
I set up the log4net.config and run the following code at the start of my console app
namespace MyNamespace
open System.IO
open log4net.Config
module LoggerConfigure =
let configureLogging() =
FileInfo("log4net.config")
|> XmlConfigurator.Configure
|> ignore
The following ( from the link above ) is C# and I want it to be an F# class that can be called as a singleton.
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
My primary quwation is - I'm a bit stuck converting that to an F# class. How do I do that?. Any ideas you have around the whole concept is appreciated as well.
There may be cases where a more sophisticated handling of singletons is needed, but I believe that standard global let declaration in an F# module would work well enough.
You just need to make sure that the configuration code is run before the log value is accessed, which you can do by making that call as part of the let binding that defines log:
module LoggerConfigure =
let configureLogging() =
FileInfo("log4net.config")
|> XmlConfigurator.Configure
|> ignore
log4net.LogManager.GetLogger
(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)
let log = configureLogging()
Or if you prefer to put everything in a single expression:
module LoggerConfigure =
let log =
FileInfo("log4net.config") |> XmlConfigurator.Configure |> ignore
log4net.LogManager.GetLogger
(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)

Can I parse some F# code at run-time that reference types in my current assembly?

Say I have the following type defined:
type Foo = { A: string; B: int }
I want a function parse, such that:
let myfoo = parse<Foo> "{A = \"foo\"; B = 5}"
gives me an instance of type Foo (or error).
Is this possible using FSharp.Compiler.Service?
UPDATE:
While there are other questions that address parsing of F# code, they don't address having references in the current assembly.
You can do this by referencing the current assembly from the hosted F# interactive - this only works if you are running this from a compiled program (which has assembly located on disk) and if your types are public, but it may do the trick in your case.
Given the usual setup documented on the Embedding F# Interactive page, you can do something like this:
module Program
type Test = { A:int; B:string }
// (omitted code to initialize the fsi service)
let fsiSession = FsiEvaluationSession.Create(...)
// Run #r command to reference the current assembly
let loc = System.Reflection.Assembly.GetExecutingAssembly().Location
fsiSession.EvalInteraction(sprintf "#r #\"%s\"" loc)
// Open the module or namespace containing your types
fsiSession.EvalInteraction("open Program")
// Evaluate code using the type and cast it back to our type
let value = fsiSession.EvalExpression("{A=0; B=\"hi\"}").Value.ReflectionValue :?> Test
printfn "%A" value

How to parse a TypeScript code base into ASTs

I want to parse TypeScript projects into ASTs.
I can parse single file by :
import ts = require("typescript");
var fs = require('fs');
var util = require('util');
const ast = ts.createSourceFile('sample.ts', fs.readFileSync('sample.ts').toString(), ts.ScriptTarget.ES6, true);
console.log("AST:"+util.inspect(ast));
I can even loop through the files and filter files by extension and run above code to generate ASt.
However I want to parse the whole project in such a way that the relationships (like imports) will be preserved in AST.
For example:
If, a.ts is referencing var x from b.ts as below:
a.ts:
var y = x;
b.ts:
var x = 5;
In this case signature of x in a .ts should be resolved as : b.ts.x or equivalent.
I just want all such relationships resolved in the ASts as I parse the .ts files one by one.
You can load your project using
ts.createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program)
rootNames is the list of all typescript files in your project. As far as I know, unless you declare type explicitly, AST will have no reference to it.
for eg. If you have
class MyClass {
// some code
}
let instance1 = new MyClass();
let instance2: MyClass = new MyClass();
In AST, node for instance1 will have type property as undefined, for instance2 type property will have proper TypeReference
For type checking you can use Program.getTypeChecker(). This returns a TypeChecker which can be used to analyse files in the program.

Executing scripts in a grails Job

I am working on two parts of a program. One that creates small groovy scripts and another that executes them in a Job. Each script is used to convert information from a map to a Domain object. The job will then save the domain object for future use.
Here is a small example.
Domain
class Report {
Date date
Country country
}
Map
Map<String, String> map = new HashMap<String, String>();
map.put("date", "2015-04-21 11:31:11");
map.put("country", "United States");
Date Script
String script = "x.date = Date.parse('y-M-d h:m:s', y['date'])"
The script is currently executed using Eval.
Eval(report, map, script)
There are other more complicated scripts that need to look up information and make decisions based on values.
The date script works fine but when the country script executes I get an error.
Country Script
String script = "import app.Country\nx.country = Country.findByName(y['country'])"
Error
Script1.groovy: 1: unable to resolve class app.Country
It seems like the Country class is not getting loaded in the call to Eval. How can I do an import in Eval? Should I use Eval or GroovyShell?
You need to instantiate a GroovyShell passing as argument the grailsApplication class loader, see the example below:
Bootstrap.groovy
Map bindingValues = new HashMap()
bindingValues.sessionFactory = sessionFactory
bindingValues.propertyInstanceMap = propertyInstanceMap
bindingValues.dataSource = dataSource
bindingValues.ctx = grailsApplication.mainContext
GroovyShell gs = new GroovyShell(grailsApplication.classLoader, new Binding(bindingValues))
gs.evaluate new File("docs/CargaAvaliacoes.groovy")
CargaAvaliacoes.groovy
import avaliacao.Colaborador
import modelo.ModeloAvaliacao
import programa.Programa
def programa = Programa.get(1)
def modelo = ModeloAvaliacao.get(1)
def avaliadores = ["02270789332":"1020016","11388449681":"1010002","02231772331":"1010004","04247774332":"1020002"]
def avaliacaoService = ctx.getBean("avaliacaoService")
avaliadores.each {
def avaliador = Colaborador.findByCpf(it.key)
def avaliados = Colaborador.findAllBySetorAndCpfNotEqual(it.value,it.key)
avaliados.each {
avaliacaoService.cadastrar(programa, modelo, avaliador, it)
}
}
** You need to specify the grailsApplication.classLoader because this class loader is used to load the Grails domain classes.

Auto open a namespace in F#

Hi would like to know how to make the f# compiler to auto open a namespace automatically.
I have
namespace XXX
I have to add something here do(AutoOpen("XXX.YYY")) or something like that to make the XXX.YYY module to be opened when referencing the library from external projects.
[<AutoOpen>]
module YYY =
....
Thanks
In order to open a namespace/module without opening its parent first, you have to add the attribute on assembly level.
You can do this by adding an AssemblyInfo.fs file to your project:
In the case of the following code:
namespace Framework
module GlobalFunctions =
let Test () =
10.
You would for instance add the following code to AssemblyInfo.fs:
namespace Framework
[<assembly:AutoOpen("Framework.GlobalFunctions")>]
do()
And then you can call the code from a script (.fsx) file with:
#r #"C:\PathToAssembly\Assembly.dll"
let result = Test ()
Resulting in:
--> Referenced 'C:\PathToAssembly\Assembly.dll'
val result : float = 10.0
The AutoOpen attribute can be applied only to F# module, so you won't be able to add it to an entire namespace. However, since you can place all F# declarations inside a module, that may be enough for what you need. The syntax is:
[<AutoOpen>]
module MyGlobals =
// Declarations in the module
type Foo() =
member x.Bar = 10
When you reference the assembly, you should see Foo immediately. If the declaration is placed inside another namespace (i.e. MyLibrary), then you'll need to add open MyLibrary, but MyGlobals will be accessible automatically.

Resources