Sunday, July 10, 2011

Fwd: run IronPython through a native application like VIM (update)

See the update below this.

On 07/02/2011 11:33 AM, Roland Puntaier wrote:
> Hi,
>
> With Python I have macros in VIM that let me execute individual Python
> lines or Python blocks of code.
> Since I'm currently working with .NET I wanted to have that with
> IronPython, too.
> Googling showed that it's very easy to embed IronPython in .NET
> applications. But VIM is a native application.
> Since I just wanted to execute IronPython code from within VIM without
> exposing VIM types to IronPython a solution was at hand.
>
> I created a mixed mode DLL (ironpythonToC.dll) in C++/CLI and exposed
> two C functions,
> one to execute code (ExecuteInIronPython) and one to retrieve a value
> (FromIronPythonAsString) (see the code below).
>
> These functions can be executed from all native applications and well,
> from C Python, too.
> Since I have Python enabled in VIM I used the latter. It allows me to
> get the text from the vim buffer.
> Via Python ctype it is possible to load the DLL and execute the text.
> Here are the Python ctype definitions of the functions (I use Python
> 3.1 on this Windows machine):
>
> ############ Python ctype definitions of function from
> ironpythonToC.dll ##################
> from ctypes import LibraryLoader,WinDLL,CFUNCTYPE,c_bool,c_wchar_p
> #ironpythonToC.dll needs to be in PATH, e.g. copy it to C:\Windows
> dll=LibraryLoader(WinDLL).LoadLibrary("ironpythonToC.dll");
> #
> pExecuteInIronPython = CFUNCTYPE(c_bool, c_wchar_p)
> paramflags = (1, "code"),
> ExecuteInIronPython = pExecuteInIronPython(("ExecuteInIronPython",
> dll), paramflags)
> #
> pFromIronPythonAsString = CFUNCTYPE(c_wchar_p, c_wchar_p)
> paramflags = (1, "name"),
> FromIronPythonAsString =
> pFromIronPythonAsString(("FromIronPythonAsString", dll), paramflags)
> #
> #example:
> #ExecuteInIronPython("t=[2,3]+[4,5]")
> #FromIronPythonAsString("t")
> ############ Python ctype definitions of function from
> ironpythonToC.dll ##################
>
> Source code of ironpythonToC.dll
> ====================
> Here is the ironpythonToC header:
>
> ////////////////////////////////////// ironpythonToC.h
> //////////////////////////////
> #pragma once
> extern "C" __declspec(dllexport) bool ExecuteInIronPython(wchar_t *code);
> extern "C" __declspec(dllexport) const wchar_t
> *FromIronPythonAsString(wchar_t *name);
> ////////////////////////////////////// end ironpythonToC.h
> //////////////////////////////
>
> And here is the implementation:
> ////////////////////////////////////// ironpythonToC.cpp
> //////////////////////////////
> #include "stdafx.h"
> #include < vcclr.h >
> #include <wchar.h>
> #include "ironpythonToC.h"
> #include <string>
>
>
> using namespace System;
> using namespace IronPython::Hosting;
> using namespace Microsoft::Scripting;
> using namespace Microsoft::Scripting::Hosting;
>
>
> public ref class IronPythonWrapper
> {
> ScriptEngine ^engine;
> ScriptScope ^scope;
>
> IronPythonWrapper()
> {
> engine = Python::CreateEngine();
> scope = engine->CreateScope();
> }
>
> public:
> static IronPythonWrapper ^Instance = gcnew IronPythonWrapper();
>
> bool Execute(System::String ^code)
> {
> bool result = true;
> try
> {
> ScriptSource ^source =
> engine->CreateScriptSourceFromString(code, SourceCodeKind::Statements);
> source->Execute(scope);
> }
> catch (System::Exception ^)
> {
> result = false;
> }
> return result;
> }
> System::String ^GetVariable(System::String ^name)
> {
> System::String ^res = "";
> try
> {
> System::String ^resAsString="res0xhshtela821h";
> Execute(resAsString+"=str("+name+")");
> Object ^o = scope->GetVariable(resAsString);
> res = o->ToString();
> }
> catch (System::Exception ^)
> {
> res = "";
> }
> return res;
> }
> };
>
> bool ExecuteInIronPython(wchar_t *code)
> {
> System::String ^codeDotNet = gcnew System::String(code);
> return IronPythonWrapper::Instance->Execute(codeDotNet);
> }
>
> const wchar_t *FromIronPythonAsString(wchar_t *name)
> {
> static wchar_t *result = NULL;
> System::String ^n = gcnew System::String(name);
> System::String ^str = IronPythonWrapper::Instance->GetVariable(n);
>
> int len = str->Length;
> pin_ptr<const wchar_t> wch = PtrToStringChars(str);
>
> if (result != NULL)
> free(result);
> result = (wchar_t *)malloc(sizeof(wchar_t)*(len+1));
>
> wcscpy_s(result,len+1,wch);
> return result;
> }
>
> ////////////////////////////////////// end ironpythonToC.cpp
> //////////////////////////////
>
> I also attach the ironpythonToC.dll itself. Move it to C:\Windows. You
> need IronPython itself installed of course.
>
> Regards,
> Roland

After working a bit with this solution a few changes to the mixed
assembly DLL were convenient, which I also want to share.
The changes are:
- replace \ by \\
- redirect stdout (print) to the variable last_printed

I've attached the whole VS2010 project and also the DLL.

How I use it in vim with python 3.1 is here in this excerpt.

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"excerpt from _vimrc
py3 << EOL
def get_pyout():
bl = [b for b in vim.buffers if b.name and b.name.find('pyout')!=-1]
if bl: bl = bl[0]
return bl
#
def print_result_in_vim(expr, result):
res_str = str(result)
vim.eval('setreg("*","{0}")'.format(res_str.replace('"','\\"')))
pyoutbuf=get_pyout()
if pyoutbuf:
res_out=['>>>'+e for e in
expr.splitlines()]+str(result).splitlines()+['']
pyoutbuf.range(0,0).append(res_out)
vim.command("wincmd b")
vim.command("normal gg")
vim.command("wincmd p")
vim.command("wincmd j")
else:
print(result)
#
def current_vim_range():
c_r=vim.current.range
if not c_r:
c_r=[vim.current.line]
b = (0,0)
e = (0,-1)
if len(c_r)==1:
b = vim.current.buffer.mark('<')
e = vim.current.buffer.mark('>')
if b and e and b[1] <= e[1] and e[1] < len(c_r[0]):
rngstr=c_r[0][b[1]:(e[1]+1)]
rngstr=rngstr.strip()
else:
t = c_r[0]
i=t.find(t.strip(' >'))
rngstr='\n'.join([ln[i:] for ln in c_r])
return rngstr
#
def eval_current_vim_range():
rngstr = current_vim_range()
eval(compile(rngstr,'<string>','exec'),globals())
#
def make_py_res(rngstr):
rl = rngstr.splitlines()
if rl[-1][0] != ' ':
rl[-1] = "py_res ="+rl[-1]
else:
rl[0] = "py_res = "+rl[0]
return '\n'.join(rl)
#
def print_current_vim_range():
rngstr = current_vim_range()
try:
eval(compile(make_py_res(rngstr),'<string>','exec'),globals())
result = eval("py_res",globals())
except:
eval(compile(rngstr,'<string>','exec'),globals())
result = None
print_result_in_vim(rngstr,result)
#
#ipy:
from ctypes import LibraryLoader,WinDLL,CFUNCTYPE,c_bool,c_wchar_p
#ironpythonToC.dll needs to be in PATH, e.g. C:\Windows
dll=LibraryLoader(WinDLL).LoadLibrary("ironpythonToC.dll");
#
pExecuteInIronPython = CFUNCTYPE(c_bool, c_wchar_p)
paramflags = (1, "code"),
ExecuteInIronPython = pExecuteInIronPython(("ExecuteInIronPython", dll),
paramflags)
#
pFromIronPythonAsString = CFUNCTYPE(c_wchar_p, c_wchar_p)
paramflags = (1, "name"),
FromIronPythonAsString =
pFromIronPythonAsString(("FromIronPythonAsString", dll), paramflags)
#
#ExecuteInIronPython("t=[2,3]+[4,5]")
#FromIronPythonAsString("t")
#
def ipy(r):
return 'ExecuteInIronPython("""'+r+'""")'
#
def ipy_eval_current_vim_range():
rngstr = ipy(current_vim_range())
eval(compile(rngstr,'<string>','exec'),globals())
#
def ipy_print_current_vim_range():
rngstr = current_vim_range()
try:
eval(compile(ipy(make_py_res(rngstr)),'<string>','exec'),globals())
result = eval('FromIronPythonAsString("py_res")',globals())
except:
eval(compile(ipy(rngstr),'<string>','exec'),globals())
result = None
print_result_in_vim(rngstr,result)
#
def ipy_get_last_printed():
result = eval('FromIronPythonAsString("last_printed")',globals())
print_result_in_vim("last_printed",result)
#
EOL
map \j :vert rightb new pyout:setlocal buftype=nofile bufhidden=wipe
nobuflisted noswapfile<C-W><C-W>
map <C-j> :py3 print_current_vim_range()<CR>
map <C-k> :py3 eval_current_vim_range()<CR>
map <M-j> :py3 ipy_print_current_vim_range()<CR>
map <M-k> :py3 ipy_eval_current_vim_range()<CR>
map <M-l> :py3 ipy_eval_current_vim_range()<CR>:py3
ipy_get_last_printed()<CR>

"end of excerpt


--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

No comments: