Andrew Rimmer
Andrew Rimmer

Reputation: 3922

How to use IronPython with App.Config?

I have a class library that is usually called from a .net console or web application. It integrates with various components, and relies on an app.config or web.config.

If I want to utilise the class library from script (i.e. IronPython), how can I get the script to utilise the config file? Ideally I want to be able to choose the config file when I run the script, or by convention (config file sitting alongside the script file).

I don't want to change the ipy.exe.config if possible as this wouldn't scale for multiple configurations without having multiple copies of IronPython?

Any alternatives?

Upvotes: 21

Views: 5006

Answers (6)

mattcschmidt
mattcschmidt

Reputation: 81

I attempted to follow the answers above, but found it too complex. If you know exactly what attribute you need from your App.config file, then you can place it directly in the code. For instance, a dll I had imported needed to know the AssemblyPath attribute in my App.Config file.

import clr
import System.Configuration
clr.AddReference("System.Configuration")
from System.Configuration import ConfigurationManager

ConfigurationManager.AppSettings["AssemblyPath"] = 'C:/Program Files (X86)/...

This was all I needed, and the class library I was connecting to was able to see the AssemblyPath attribute it needed to run.

Upvotes: 0

Moshe
Moshe

Reputation: 2668

I have a working solution with code sample. See my blog: http://technomosh.blogspot.com/2012/01/using-appconfig-in-ironpython.html

It requires a special proxy class which is injected to the ConfigurationManager.

Here is the source for the ConfigurationProxy library:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Configuration.Internal;
using System.Xml;
using System.Collections.Specialized;
using System.Reflection;
using System.IO;

namespace IronPythonUtilities
{
    /// <summary>
    /// A custom app.config injector for use with IronPython code that needs configuration files.
    /// The code was taken and modified from the great work by Tom E Stephens:
    /// http://tomestephens.com/2011/02/making-ironpython-work-overriding-the-configurationmanager/
    /// </summary>
    public sealed class ConfigurationProxy : IInternalConfigSystem
    {
        Configuration config;
        Dictionary<string, IConfigurationSectionHandler> customSections;

        // this is called filename but really it's the path as needed...
        // it defaults to checking the directory you're running in.
        public ConfigurationProxy(string fileName)
        {
            customSections = new Dictionary<string, IConfigurationSectionHandler>();

            if (!Load(fileName))
                throw new ConfigurationErrorsException(string.Format(
                    "File: {0} could not be found or was not a valid cofiguration file.",
                    config.FilePath));
        }

        private bool Load(string file)
        {
            var map = new ExeConfigurationFileMap { ExeConfigFilename = file };
            config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

            var xml = new XmlDocument();
            using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read))
                xml.Load(stream);

            //var cfgSections = xml.GetElementsByTagName("configSections");

            //if (cfgSections.Count > 0)
            //{
            //    foreach (XmlNode node in cfgSections[0].ChildNodes)
            //    {
            //        var type = System.Activator.CreateInstance(
            //                             Type.GetType(node.Attributes["type"].Value))
            //                             as IConfigurationSectionHandler;

            //        if (type == null) continue;

            //        customSections.Add(node.Attributes["name"].Value, type);
            //    }
            //}

            return config.HasFile;
        }

        public Configuration Configuration
        {
            get { return config; }
        }

        #region IInternalConfigSystem Members

        public object GetSection(string configKey)
        {
            if (configKey == "appSettings")
                return BuildAppSettings();

            object sect = config.GetSection(configKey);

            if (customSections.ContainsKey(configKey) && sect != null)
            {
                var xml = new XmlDocument();

                xml.LoadXml(((ConfigurationSection)sect).SectionInformation.GetRawXml());
                // I have no idea what I should normally be passing through in the first
                // two params, but I never use them in my confighandlers so I opted not to
                // worry about it and just pass through something...
                sect = customSections[configKey].Create(config,
                                       config.EvaluationContext,
                                       xml.FirstChild);
            }

            return sect;
        }

        public void RefreshConfig(string sectionName)
        {
            // I suppose this will work. Reload the whole file?
            Load(config.FilePath);
        }

        public bool SupportsUserConfig
        {
            get { return false; }
        }

        #endregion

        private NameValueCollection BuildAppSettings()
        {
            var coll = new NameValueCollection();

            foreach (var key in config.AppSettings.Settings.AllKeys)
                coll.Add(key, config.AppSettings.Settings[key].Value);

            return coll;
        }

        public bool InjectToConfigurationManager()
        {
            // inject self into ConfigurationManager
            var configSystem = typeof(ConfigurationManager).GetField("s_configSystem",
                                            BindingFlags.Static | BindingFlags.NonPublic);
            configSystem.SetValue(null, this);

            // lame check, but it's something
            if (ConfigurationManager.AppSettings.Count == config.AppSettings.Settings.Count)
                return true;

            return false;
        }
    }
}

and here is how it can be loaded from Python:

import clr
clr.AddReferenceToFile('ConfigurationProxy.dll')

from IronPythonUtilities import ConfigurationProxy

def override(filename):
    proxy = ConfigurationProxy(filename)
    return proxy.InjectToConfigurationManager()

Finally, a usage sample:

import configproxy
import sys

if not configproxy.override('blogsample.config'):
    print "could not load configuration file"
    sys.exit(1)

import clr
clr.AddReference('System.Configuration')
from System.Configuration import *
connstr = ConfigurationManager.ConnectionStrings['TestConnStr']
print "The configuration string is {0}".format(connstr)

Upvotes: 4

Marko
Marko

Reputation: 93

For a workaround what I did was fill the AppSettings collection for the ConfigurationManager static class "manually", so I created a PY Script and run an "import" of it on IronPython and the settings then will be available for the class library. However I couldn assing values to the ConnectionStrings collection :(

my script looks like this

import clr
clr.AddReferenceToFileAndPath(r'c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.configuration.dll')
from System.Configuration import *
ConfigurationManager.AppSettings["settingA"] = "setting A value here"
ConfigurationManager.AppSettings["settingB"] = "setting B value here"

It would be nice though to know a way to "load" a custom .config file to the ConfigurationManager class.

Upvotes: 0

Franci Penov
Franci Penov

Reputation: 76001

You can look at the System.Configuration.ConfigurationManager class. More specifically, OpenMappedExeConfiguration method will allow you to load any .config file of your choice. This will give you a Configuration object which exposes the standard AppSettins, ConnectionStrings, SectionGroups and Sections properties.

This approach requires you to pass the name of the config file to your script as a command line argument or to have a code logic to choose the .config file at run-time.

I know no Python, so I would refrain from attempts to post sample code. :-)

Upvotes: 2

Liam Westley
Liam Westley

Reputation: 406

You can always include additional sections within config files. In your ipy.exe.config file you can add an include to import external config settings; say myApp.config.

In a batch/command file you can always copy over a specific .config set into myApp.config and therefore run with different config files on demand.

Have a peek at this blog on how to achieve this; http://weblogs.asp.net/pwilson/archive/2003/04/09/5261.aspx

Upvotes: 0

oefe
oefe

Reputation: 19916

Translating this blog post into Python, this should work:

import clr
import System.AppDomain
System.AppDomain.CurrentDomain.SetData(“APP_CONFIG_FILE”, r”c:\your\app.config”)

Upvotes: 0

Related Questions