I'm trying to add [rlcompleter][1] functionality to my fork of the IronTextBoxControl2 in RevitPythonShell.

However, I'm having a hard trouble with the line:

import __main__

in rlcompleter.py. This is because I don't have such a module. I have been searching / sifting through the IronPython 2.0 codebase trying to figure out how to publish a module.

It seems I can do so from an instance of IronPython.Runtime.PythonContext with the method PublishModule(string name, PythonModule module). But I'm having trouble with the following:

  • obtaining a PythonContext instance
  • obtaining a PythonModule instance

What I have, is a ScriptEngine and a ScriptScope. I'd like the scope to be published as the module __main__. As I understand, the scope is already attached to the module, but I can't figure out how to access it.

Here is the point in code I'd like to tie this together:

    /// <summary>
    /// Set up an IronPython environment - for interactive shell or for canned scripts
    /// </summary>
    public void SetupEnvironment(ScriptEngine engine, ScriptScope scope)
    {                        
        // add variables from Revit
        scope.SetVariable("__revit__", _commandData.Application);
        scope.SetVariable("__commandData__", _commandData);
        scope.SetVariable("__message__", _message);
        scope.SetVariable("__elements__", _elements);
        scope.SetVariable("__result__", (int)Result.Succeeded);                        

        // add preconfigures variables
        scope.SetVariable("__vars__", RevitPythonShellApplication.GetVariables());

        /* register scope as __main__ module here?! */         

        // add the search paths
        AddSearchPaths(engine);
    }

Accepted Answer

Never mind, I went through the IronPython 2.0.3 source code and came up with the following:

  • a PythonContext is a Microsoft.Scripting.Runtime.LanguageContext
  • the language context of a ScriptEngine can be obtained through the Microsoft.Scripting.Hosting.Providers.HostingHelpers.GetLanguageContext(ScriptEngine) method
  • if you have a ScriptEngine for python, well, then you can just cast.

Wow. So now I finally have my PythonContext! So I'm going to call CreateModule and PublishModule and be home before tea!

Wait. No. First, the second argument to CreateModule is a Microsoft.Scripting.Runtime.Scope. All we have is a Microsoft.Scripting.Hosting.ScriptScope. Which happens to contain a Scope, except its accessor is internal. So we need to do some reflection first.

Here is my sample code augmented by my proposed solution:

/// <summary>
/// Set up an IronPython environment - for interactive shell or for canned scripts
/// </summary>
public void SetupEnvironment(ScriptEngine engine, ScriptScope scriptScope)
{
    // add variables from Revit
    scriptScope.SetVariable("__revit__", _commandData.Application);
    scriptScope.SetVariable("__commandData__", _commandData);
    scriptScope.SetVariable("__message__", _message);
    scriptScope.SetVariable("__elements__", _elements);
    scriptScope.SetVariable("__result__", (int)Result.Succeeded);           

    // add preconfigures variables
    scriptScope.SetVariable("__vars__", RevitPythonShellApplication.GetVariables());

    // add the current scope as module '__main__'
    var languageContext = Microsoft.Scripting.Hosting.Providers.HostingHelpers.GetLanguageContext(engine);
    var pythonContext = (IronPython.Runtime.PythonContext)languageContext;
    var module = pythonContext.CreateModule(null, GetScope(scriptScope), null, IronPython.Runtime.ModuleOptions.None);            
    pythonContext.PublishModule("__main__", module);

    // add the search paths
    AddSearchPaths(engine);
}

/// <summary>
/// Be nasty and reach into the ScriptScope to get at its private '_scope' member,
/// since the accessor 'ScriptScope.Scope' was defined 'internal'.
/// </summary>
private Microsoft.Scripting.Runtime.Scope GetScope(ScriptScope scriptScope)
{
    var field = scriptScope.GetType().GetField(
        "_scope", 
        System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    return (Microsoft.Scripting.Runtime.Scope) field.GetValue(scriptScope);
}

I kept all the namespaces in the types on purpose. I find the IronPython code kind of hard to follow and often get confused about what is defined where.

Written by Daren Thomas
This page was build to provide you fast access to the question and the direct accepted answer.
The content is written by members of the stackoverflow.com community.
It is licensed under cc-wiki