import clr
clr.AddReference('IronPython')
clr.AddReference('System.Data')
from System.Data import DataTable
from System import Array, Type, Reflection
TypeArray = Array[Type]

# initialize global constants & variables
ASSEMBLY_NAME = 'MyDynamicAssembly'
MODULE_NAME = 'DynamicModule'
from System import AppDomain
_domain = AppDomain.CurrentDomain
_assemblyname = Reflection.AssemblyName()
_assemblyname.Name = ASSEMBLY_NAME
_assembly = _domain.DefineDynamicAssembly(_assemblyname, Reflection.Emit.AssemblyBuilderAccess.Run)
_module = _assembly.DefineDynamicModule(MODULE_NAME)


# I'm usinge DataTable as an example of class that need to be extended
class PyDataTable(DataTable):
    def show(self):
        print 'Extended !'

def define_type(module, name, base):
    attrs = Reflection.TypeAttributes.Public | \
            Reflection.TypeAttributes.Class | \
            Reflection.TypeAttributes.AutoClass | \
            Reflection.TypeAttributes.AnsiClass | \
            Reflection.TypeAttributes.BeforeFieldInit | \
            Reflection.TypeAttributes.AutoLayout
    return module.DefineType(name, attrs, base)

def define_field(t, name, typ):
    attrs = Reflection.FieldAttributes.Public | \
            Reflection.FieldAttributes.Static
    return t.DefineField(name, typ, attrs)

def define_constructor(t):
    attrs = Reflection.MethodAttributes.Public | \
            Reflection.MethodAttributes.HideBySig
    return t.DefineConstructor(attrs, Reflection.CallingConventions.Standard, TypeArray([]))

def common_wrap(BaseClass, name, dynamictype):
    # // use System.Reflection.Emit to make a class
    # // C#
    # class <<name>>: <<BaseClass>>
    # {
    #     public static <<dynamictype>> pyType;  // reference to UserType or PythonType
    #     public <<name>>(): base(pyType)        // call base constructor with pyType as argument
    #     {
    #     }
    # }
    global _module
    from IronPython.Runtime import Types
    dytype = clr.GetClrType(dynamictype)
    base = clr.GetClrType(BaseClass)
    t = define_type(_module, name, base)
    pyType = define_field(t, 'pyType', dytype)
    base_ctor = base.GetConstructor(TypeArray([dytype]))
    ctor = define_constructor(t)
    g = ctor.GetILGenerator()
    from System.Reflection.Emit.OpCodes import Ldarg_0, Ldsfld, Call, Ret
    g.Emit(Ldarg_0)
    g.Emit(Ldsfld, pyType)
    g.Emit(Call, base_ctor)
    g.Emit(Ret)
    r = clr.GetPythonType(t.CreateType())
    r.pyType = BaseClass
    return r

def wrap_1(BaseClass, name):
    from IronPython.Runtime import Types
    return common_wrap(BaseClass, name, Types.UserType)

def wrap_2(BaseClass, name):
    from IronPython.Runtime import Types
    return common_wrap(BaseClass, name, Types.PythonType)
    
def wrap(BaseClass, name):
    import sys
    if 'IronPython 2.0' in sys.version:
        return wrap_2(BaseClass, name)
    else:
        return wrap_1(BaseClass, name)
                                                      
WrappedPyDataTable = wrap(PyDataTable, 'WrappedPyDataTable')
w = WrappedPyDataTable()

print 'in 2.0 any action perform to "w" will cause system to halt'
w.show()
