PageRenderTime 61ms CodeModel.GetById 17ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 1ms

/IronPython_Main/Languages/IronPython/IronPython/Runtime/ClrModule.cs

#
C# | 1171 lines | 751 code | 183 blank | 237 comment | 150 complexity | 74060dfb9bc935d8c8f0fed6bbafe1c8 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* ****************************************************************************
   2 *
   3 * Copyright (c) Microsoft Corporation. 
   4 *
   5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
   6 * copy of the license can be found in the License.html file at the root of this distribution. If 
   7 * you cannot locate the  Apache License, Version 2.0, please send an email to 
   8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
   9 * by the terms of the Apache License, Version 2.0.
  10 *
  11 * You must not remove this notice, or any other, from this software.
  12 *
  13 *
  14 * ***************************************************************************/
  15
  16using System;
  17using System.Collections;
  18using System.Collections.Generic;
  19using System.IO;
  20using System.Reflection;
  21using System.Runtime.CompilerServices;
  22using System.Runtime.InteropServices;
  23using System.Text;
  24using System.Xml;
  25
  26using Microsoft.Scripting;
  27using Microsoft.Scripting.Actions;
  28using Microsoft.Scripting.Generation;
  29using Microsoft.Scripting.Runtime;
  30using Microsoft.Scripting.Utils;
  31
  32using IronPython.Runtime;
  33using IronPython.Runtime.Exceptions;
  34using IronPython.Runtime.Operations;
  35using IronPython.Runtime.Types;
  36
  37#if !SILVERLIGHT
  38using ComTypeLibInfo = Microsoft.Scripting.ComInterop.ComTypeLibInfo;
  39using ComTypeLibDesc = Microsoft.Scripting.ComInterop.ComTypeLibDesc;
  40using System.Runtime.Serialization.Formatters.Binary;
  41#endif
  42
  43[assembly: PythonModule("clr", typeof(IronPython.Runtime.ClrModule))]
  44namespace IronPython.Runtime {
  45    /// <summary>
  46    /// this class contains objecs and static methods used for
  47    /// .NET/CLS interop with Python.  
  48    /// </summary>
  49    public static class ClrModule {
  50        [SpecialName]
  51        public static void PerformModuleReload(PythonContext/*!*/ context, PythonDictionary/*!*/ dict) {
  52            if (!dict.ContainsKey("References")) {
  53                dict["References"] = context.ReferencedAssemblies;
  54            }
  55        }
  56
  57        #region Public methods
  58
  59        /// <summary>
  60        /// Gets the current ScriptDomainManager that IronPython is loaded into.  The
  61        /// ScriptDomainManager can then be used to work with the language portion of the
  62        /// DLR hosting APIs.
  63        /// </summary>
  64        public static ScriptDomainManager/*!*/ GetCurrentRuntime(CodeContext/*!*/ context) {
  65            return context.LanguageContext.DomainManager;
  66        }
  67
  68        [Documentation(@"Adds a reference to a .NET assembly.  Parameters can be an already loaded
  69Assembly object, a full assembly name, or a partial assembly name. After the
  70load the assemblies namespaces and top-level types will be available via 
  71import Namespace.")]
  72        public static void AddReference(CodeContext/*!*/ context, params object[] references) {
  73            if (references == null) throw new TypeErrorException("Expected string or Assembly, got NoneType");
  74            if (references.Length == 0) throw new ValueErrorException("Expected at least one name, got none");
  75            ContractUtils.RequiresNotNull(context, "context");
  76
  77            foreach (object reference in references) {
  78                AddReference(context, reference);
  79            }
  80        }
  81
  82        [Documentation(@"Adds a reference to a .NET assembly.  One or more assembly names can
  83be provided.  The assembly is searched for in the directories specified in 
  84sys.path and dependencies will be loaded from sys.path as well.  The assembly 
  85name should be the filename on disk without a directory specifier and 
  86optionally including the .EXE or .DLL extension. After the load the assemblies 
  87namespaces and top-level types will be available via import Namespace.")]
  88        public static void AddReferenceToFile(CodeContext/*!*/ context, params string[] files) {
  89            if (files == null) throw new TypeErrorException("Expected string, got NoneType");
  90            if (files.Length == 0) throw new ValueErrorException("Expected at least one name, got none");
  91            ContractUtils.RequiresNotNull(context, "context");
  92
  93            foreach (string file in files) {
  94                AddReferenceToFile(context, file);
  95            }
  96        }
  97
  98        [Documentation(@"Adds a reference to a .NET assembly.  Parameters are an assembly name. 
  99After the load the assemblies namespaces and top-level types will be available via 
 100import Namespace.")]
 101        public static void AddReferenceByName(CodeContext/*!*/ context, params string[] names) {
 102            if (names == null) throw new TypeErrorException("Expected string, got NoneType");
 103            if (names.Length == 0) throw new ValueErrorException("Expected at least one name, got none");
 104            ContractUtils.RequiresNotNull(context, "context");
 105
 106            foreach (string name in names) {
 107                AddReferenceByName(context, name);
 108            }
 109        }
 110
 111#if !SILVERLIGHT // files, paths
 112
 113        /// <summary>
 114        /// LoadTypeLibrary(rcw) -> type lib desc
 115        /// 
 116        /// Gets an ITypeLib object from OLE Automation compatible RCW ,
 117        /// reads definitions of CoClass'es and Enum's from this library
 118        /// and creates an object that allows to instantiate coclasses
 119        /// and get actual values for the enums.
 120        /// </summary>
 121        public static ComTypeLibInfo LoadTypeLibrary(CodeContext/*!*/ context, object rcw) {
 122            return ComTypeLibDesc.CreateFromObject(rcw);
 123        }
 124
 125        /// <summary>
 126        /// LoadTypeLibrary(guid) -> type lib desc
 127        /// 
 128        /// Reads the latest registered type library for the corresponding GUID,
 129        /// reads definitions of CoClass'es and Enum's from this library
 130        /// and creates a IDynamicMetaObjectProvider that allows to instantiate coclasses
 131        /// and get actual values for the enums.
 132        /// </summary>
 133        public static ComTypeLibInfo LoadTypeLibrary(CodeContext/*!*/ context, Guid typeLibGuid) {
 134            return ComTypeLibDesc.CreateFromGuid(typeLibGuid);
 135        }
 136
 137        /// <summary>
 138        /// AddReferenceToTypeLibrary(rcw) -> None
 139        /// 
 140        /// Makes the type lib desc available for importing. See also LoadTypeLibrary.
 141        /// </summary>
 142        public static void AddReferenceToTypeLibrary(CodeContext/*!*/ context, object rcw) {
 143            ComTypeLibInfo typeLibInfo;
 144            typeLibInfo = ComTypeLibDesc.CreateFromObject(rcw);
 145            PublishTypeLibDesc(context, typeLibInfo.TypeLibDesc);
 146        }
 147
 148        /// <summary>
 149        /// AddReferenceToTypeLibrary(guid) -> None
 150        /// 
 151        /// Makes the type lib desc available for importing.  See also LoadTypeLibrary.
 152        /// </summary>
 153        public static void AddReferenceToTypeLibrary(CodeContext/*!*/ context, Guid typeLibGuid) {
 154            ComTypeLibInfo typeLibInfo;
 155            typeLibInfo = ComTypeLibDesc.CreateFromGuid(typeLibGuid);
 156            PublishTypeLibDesc(context, typeLibInfo.TypeLibDesc);
 157        }
 158
 159        [Documentation(@"Adds a reference to a .NET assembly.  Parameters are a partial assembly name. 
 160After the load the assemblies namespaces and top-level types will be available via 
 161import Namespace.")]
 162        public static void AddReferenceByPartialName(CodeContext/*!*/ context, params string[] names) {
 163            if (names == null) throw new TypeErrorException("Expected string, got NoneType");
 164            if (names.Length == 0) throw new ValueErrorException("Expected at least one name, got none");
 165            ContractUtils.RequiresNotNull(context, "context");
 166
 167            foreach (string name in names) {
 168                AddReferenceByPartialName(context, name);
 169            }
 170        }
 171
 172        [Documentation(@"Adds a reference to a .NET assembly.  Parameters are a full path to an. 
 173assembly on disk. After the load the assemblies namespaces and top-level types 
 174will be available via import Namespace.")]
 175#if CLR2
 176        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFile")]
 177        public static Assembly/*!*/ LoadAssemblyFromFileWithPath(string/*!*/ file) {
 178            if (file == null) throw new TypeErrorException("LoadAssemblyFromFileWithPath: arg 1 must be a string.");
 179            // We use Assembly.LoadFile instead of Assembly.LoadFrom as the latter first tries to use Assembly.Load
 180            return Assembly.LoadFile(file);
 181        }
 182#else
 183        public static Assembly/*!*/ LoadAssemblyFromFileWithPath(CodeContext/*!*/ context, string/*!*/ file) {
 184            if (file == null) throw new TypeErrorException("LoadAssemblyFromFileWithPath: arg 1 must be a string.");
 185            
 186            Assembly res;
 187            if (!context.LanguageContext.TryLoadAssemblyFromFileWithPath(file, out res)) {
 188                if (!Path.IsPathRooted(file)) {
 189                    throw new ValueErrorException("LoadAssemblyFromFileWithPath: path must be rooted");
 190                } else if (!File.Exists(file)) {
 191                    throw new ValueErrorException("LoadAssemblyFromFileWithPath: file not found");
 192                } else {
 193                    throw new ValueErrorException("LoadAssemblyFromFileWithPath: error loading assembly");
 194                }
 195            }
 196            return res;
 197        }
 198#endif
 199
 200        [Documentation(@"Loads an assembly from the specified filename and returns the assembly
 201object.  Namespaces or types in the assembly can be accessed directly from 
 202the assembly object.")]
 203        public static Assembly/*!*/ LoadAssemblyFromFile(CodeContext/*!*/ context, string/*!*/ file) {
 204            if (file == null) throw new TypeErrorException("Expected string, got NoneType");
 205            if (file.Length == 0) throw new ValueErrorException("assembly name must not be empty string");
 206            ContractUtils.RequiresNotNull(context, "context");
 207
 208            if (file.IndexOf(System.IO.Path.DirectorySeparatorChar) != -1) {
 209                throw new ValueErrorException("filenames must not contain full paths, first add the path to sys.path");
 210            }
 211
 212            return context.LanguageContext.LoadAssemblyFromFile(file);
 213        }
 214
 215        [Documentation(@"Loads an assembly from the specified partial assembly name and returns the 
 216assembly object.  Namespaces or types in the assembly can be accessed directly 
 217from the assembly object.")]
 218        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadWithPartialName")]
 219        public static Assembly/*!*/ LoadAssemblyByPartialName(string/*!*/ name) {
 220            if (name == null) {
 221                throw new TypeErrorException("LoadAssemblyByPartialName: arg 1 must be a string");
 222            }
 223
 224#pragma warning disable 618 // csc
 225#pragma warning disable 612 // gmcs
 226            return Assembly.LoadWithPartialName(name);
 227#pragma warning restore 618
 228#pragma warning restore 612
 229        }
 230#endif
 231
 232        [Documentation(@"Loads an assembly from the specified assembly name and returns the assembly
 233object.  Namespaces or types in the assembly can be accessed directly from 
 234the assembly object.")]
 235        public static Assembly/*!*/ LoadAssemblyByName(CodeContext/*!*/ context, string/*!*/ name) {
 236            if (name == null) {
 237                throw new TypeErrorException("LoadAssemblyByName: arg 1 must be a string");
 238            }
 239
 240            return PythonContext.GetContext(context).DomainManager.Platform.LoadAssembly(name);
 241        }
 242
 243        /// <summary>
 244        /// Use(name) -> module
 245        /// 
 246        /// Attempts to load the specified module searching all languages in the loaded ScriptRuntime.
 247        /// </summary>
 248        public static object Use(CodeContext/*!*/ context, string/*!*/ name) {
 249            ContractUtils.RequiresNotNull(context, "context");
 250
 251            if (name == null) {
 252                throw new TypeErrorException("Use: arg 1 must be a string");
 253            }
 254
 255            var scope = Importer.TryImportSourceFile(PythonContext.GetContext(context), name);
 256            if (scope == null) {
 257                throw new ValueErrorException(String.Format("couldn't find module {0} to use", name));
 258            }
 259            return scope;
 260        }
 261
 262        /// <summary>
 263        /// Use(path, language) -> module
 264        /// 
 265        /// Attempts to load the specified module belonging to a specific language loaded into the
 266        /// current ScriptRuntime.
 267        /// </summary>
 268        public static object/*!*/ Use(CodeContext/*!*/ context, string/*!*/ path, string/*!*/ language) {
 269            ContractUtils.RequiresNotNull(context, "context");
 270
 271            if (path == null) {
 272                throw new TypeErrorException("Use: arg 1 must be a string");
 273            }
 274
 275            if (language == null) {
 276                throw new TypeErrorException("Use: arg 2 must be a string");
 277            }
 278
 279            var manager = context.LanguageContext.DomainManager;
 280            if (!manager.Platform.FileExists(path)) {
 281                throw new ValueErrorException(String.Format("couldn't load module at path '{0}' in language '{1}'", path, language));
 282            }
 283
 284            var sourceUnit = manager.GetLanguageByName(language).CreateFileUnit(path);
 285            return Importer.ExecuteSourceUnit(context.LanguageContext, sourceUnit);
 286        }
 287
 288        /// <summary>
 289        /// SetCommandDispatcher(commandDispatcher)
 290        /// 
 291        /// Sets the current command dispatcher for the Python command line.  
 292        /// 
 293        /// The command dispatcher will be called with a delegate to be executed.  The command dispatcher
 294        /// should invoke the target delegate in the desired context.
 295        /// 
 296        /// A common use for this is to enable running all REPL commands on the UI thread while the REPL
 297        /// continues to run on a non-UI thread.
 298        /// </summary>
 299        public static Action<Action> SetCommandDispatcher(CodeContext/*!*/ context, Action<Action> dispatcher) {
 300            ContractUtils.RequiresNotNull(context, "context");
 301
 302            return ((PythonContext)context.LanguageContext).GetSetCommandDispatcher(dispatcher);
 303        }
 304
 305
 306        public static void ImportExtensions(CodeContext/*!*/ context, PythonType type) {
 307            if (type == null) {
 308                throw PythonOps.TypeError("type must not be None");
 309            } else if (!type.IsSystemType) {
 310                throw PythonOps.ValueError("type must be .NET type");
 311            }
 312
 313            lock (context.ModuleContext) {
 314                context.ModuleContext.ExtensionMethods = ExtensionMethodSet.AddType(context.LanguageContext, context.ModuleContext.ExtensionMethods, type);
 315            }
 316        }
 317
 318        public static void ImportExtensions(CodeContext/*!*/ context, [NotNull]NamespaceTracker @namespace) {
 319            lock (context.ModuleContext) {
 320                context.ModuleContext.ExtensionMethods = ExtensionMethodSet.AddNamespace(context.LanguageContext, context.ModuleContext.ExtensionMethods, @namespace);
 321            }
 322        }
 323
 324        #endregion
 325
 326        #region Private implementation methods
 327
 328        private static void AddReference(CodeContext/*!*/ context, object reference) {
 329            Assembly asmRef = reference as Assembly;
 330            if (asmRef != null) {
 331                AddReference(context, asmRef);
 332                return;
 333            }
 334
 335            string strRef = reference as string;
 336            if (strRef != null) {
 337                AddReference(context, strRef);
 338                return;
 339            }
 340
 341            throw new TypeErrorException(String.Format("Invalid assembly type. Expected string or Assembly, got {0}.", reference));
 342        }
 343
 344        private static void AddReference(CodeContext/*!*/ context, Assembly assembly) {
 345            context.LanguageContext.DomainManager.LoadAssembly(assembly);
 346        }
 347
 348        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] // TODO: fix
 349        private static void AddReference(CodeContext/*!*/ context, string name) {
 350            if (name == null) throw new TypeErrorException("Expected string, got NoneType");
 351
 352            Assembly asm = null;
 353
 354            try {
 355                asm = LoadAssemblyByName(context, name);
 356            } catch { }
 357
 358            // note we don't explicit call to get the file version
 359            // here because the assembly resolve event will do it for us.
 360
 361#if !SILVERLIGHT // files, paths
 362            if (asm == null) {
 363                asm = LoadAssemblyByPartialName(name);
 364            }
 365#endif
 366
 367            if (asm == null) {
 368                throw new IOException(String.Format("Could not add reference to assembly {0}", name));
 369            }
 370            AddReference(context, asm);
 371        }
 372
 373        private static void AddReferenceToFile(CodeContext/*!*/ context, string file) {
 374            if (file == null) throw new TypeErrorException("Expected string, got NoneType");
 375
 376#if SILVERLIGHT
 377            Assembly asm = context.LanguageContext.DomainManager.Platform.LoadAssemblyFromPath(file);
 378#else
 379            Assembly asm = LoadAssemblyFromFile(context, file);
 380#endif
 381            if (asm == null) {
 382                throw new IOException(String.Format("Could not add reference to assembly {0}", file));
 383            }
 384
 385            AddReference(context, asm);
 386        }
 387
 388#if !SILVERLIGHT // files, paths
 389        private static void AddReferenceByPartialName(CodeContext/*!*/ context, string name) {
 390            if (name == null) throw new TypeErrorException("Expected string, got NoneType");
 391            ContractUtils.RequiresNotNull(context, "context");
 392
 393            Assembly asm = LoadAssemblyByPartialName(name);
 394            if (asm == null) {
 395                throw new IOException(String.Format("Could not add reference to assembly {0}", name));
 396            }
 397
 398            AddReference(context, asm);
 399        }
 400
 401        private static void PublishTypeLibDesc(CodeContext context, ComTypeLibDesc typeLibDesc) {
 402            PythonOps.ScopeSetMember(context, context.LanguageContext.DomainManager.Globals, typeLibDesc.Name, typeLibDesc);
 403        }
 404#endif
 405        private static void AddReferenceByName(CodeContext/*!*/ context, string name) {
 406            if (name == null) throw new TypeErrorException("Expected string, got NoneType");
 407
 408            Assembly asm = LoadAssemblyByName(context, name);
 409
 410            if (asm == null) {
 411                throw new IOException(String.Format("Could not add reference to assembly {0}", name));
 412            }
 413
 414            AddReference(context, asm);
 415        }
 416
 417        #endregion
 418
 419        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] // TODO: fix
 420        public sealed class ReferencesList : List<Assembly>, ICodeFormattable {
 421
 422            public new void Add(Assembly other) {
 423                base.Add(other);
 424            }
 425
 426            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists"), SpecialName]
 427            public ClrModule.ReferencesList Add(object other) {
 428                IEnumerator ie = PythonOps.GetEnumerator(other);
 429                while (ie.MoveNext()) {
 430                    Assembly cur = ie.Current as Assembly;
 431                    if (cur == null) throw PythonOps.TypeError("non-assembly added to references list");
 432
 433                    base.Add(cur);
 434                }
 435                return this;
 436            }
 437
 438            public string/*!*/ __repr__(CodeContext/*!*/ context) {
 439                StringBuilder res = new StringBuilder("(");
 440                string comma = "";
 441                foreach (Assembly asm in this) {
 442                    res.Append(comma);
 443                    res.Append('<');
 444                    res.Append(asm.FullName);
 445                    res.Append('>');
 446                    comma = "," + Environment.NewLine;
 447                }
 448
 449                res.AppendLine(")");
 450                return res.ToString();
 451            }
 452        }
 453
 454        private static PythonType _strongBoxType;
 455
 456        #region Runtime Type Checking support
 457#if !SILVERLIGHT // files, paths
 458
 459        [Documentation(@"Adds a reference to a .NET assembly.  One or more assembly names can
 460be provided which are fully qualified names to the file on disk.  The 
 461directory is added to sys.path and AddReferenceToFile is then called. After the 
 462load the assemblies namespaces and top-level types will be available via 
 463import Namespace.")]
 464        public static void AddReferenceToFileAndPath(CodeContext/*!*/ context, params string[] files) {
 465            if (files == null) throw new TypeErrorException("Expected string, got NoneType");
 466            ContractUtils.RequiresNotNull(context, "context");
 467
 468            foreach (string file in files) {
 469                AddReferenceToFileAndPath(context, file);
 470            }
 471        }
 472
 473        private static void AddReferenceToFileAndPath(CodeContext/*!*/ context, string file) {
 474            if (file == null) throw PythonOps.TypeError("Expected string, got NoneType");
 475
 476            // update our path w/ the path of this file...
 477            string path = System.IO.Path.GetDirectoryName(file);
 478            List list;
 479
 480            PythonContext pc = PythonContext.GetContext(context);
 481            if (!pc.TryGetSystemPath(out list)) {
 482                throw PythonOps.TypeError("cannot update path, it is not a list");
 483            }
 484
 485            list.append(path);
 486
 487            Assembly asm = pc.LoadAssemblyFromFile(file);
 488            if (asm == null) throw PythonOps.IOError("file does not exist: {0}", file);
 489            AddReference(context, asm);
 490        }
 491
 492#endif
 493
 494        /// <summary>
 495        /// Gets the CLR Type object from a given Python type object.
 496        /// </summary>
 497        public static Type GetClrType(Type type) {
 498            return type;
 499        }
 500
 501        /// <summary>
 502        /// Gets the Python type object from a given CLR Type object.
 503        /// </summary>
 504        public static PythonType GetPythonType(Type t) {
 505            return DynamicHelpers.GetPythonTypeFromType(t);
 506        }
 507
 508        /// <summary>
 509        /// OBSOLETE: Gets the Python type object from a given CLR Type object.
 510        /// 
 511        /// Use clr.GetPythonType instead.
 512        /// </summary>
 513        [Obsolete("Call clr.GetPythonType instead")]
 514        public static PythonType GetDynamicType(Type t) {
 515            return DynamicHelpers.GetPythonTypeFromType(t);
 516        }
 517
 518        public static PythonType Reference {
 519            get {
 520                return StrongBox;
 521            }
 522        }
 523
 524
 525        public static PythonType StrongBox {
 526            get {
 527                if (_strongBoxType == null) {
 528                    _strongBoxType = DynamicHelpers.GetPythonTypeFromType(typeof(StrongBox<>));
 529                }
 530                return _strongBoxType;
 531            }
 532        }
 533
 534        /// <summary>
 535        /// accepts(*types) -> ArgChecker
 536        /// 
 537        /// Decorator that returns a new callable object which will validate the arguments are of the specified types.
 538        /// </summary>
 539        /// <param name="types"></param>
 540        /// <returns></returns>
 541        public static object accepts(params object[] types) {
 542            return new ArgChecker(types);
 543        }
 544
 545        /// <summary>
 546        /// returns(type) -> ReturnChecker
 547        /// 
 548        /// Returns a new callable object which will validate the return type is of the specified type.
 549        /// </summary>
 550        public static object returns(object type) {
 551            return new ReturnChecker(type);
 552        }
 553
 554        public static object Self() {
 555            return null;
 556        }
 557
 558        #endregion
 559
 560        /// <summary>
 561        /// Decorator for verifying the arguments to a function are of a specified type.
 562        /// </summary>
 563        public class ArgChecker {
 564            private object[] expected;
 565
 566            public ArgChecker(object[] prms) {
 567                expected = prms;
 568            }
 569
 570            #region ICallableWithCodeContext Members
 571
 572            [SpecialName]
 573            public object Call(CodeContext context, object func) {
 574                // expect only to receive the function we'll call here.
 575
 576                return new RuntimeArgChecker(func, expected);
 577            }
 578
 579            #endregion
 580        }
 581
 582        /// <summary>
 583        /// Returned value when using clr.accepts/ArgChecker.  Validates the argument types and
 584        /// then calls the original function.
 585        /// </summary>
 586        public class RuntimeArgChecker : PythonTypeSlot {
 587            private object[] _expected;
 588            private object _func;
 589            private object _inst;
 590
 591            public RuntimeArgChecker(object function, object[] expectedArgs) {
 592                _expected = expectedArgs;
 593                _func = function;
 594            }
 595
 596            public RuntimeArgChecker(object instance, object function, object[] expectedArgs)
 597                : this(function, expectedArgs) {
 598                _inst = instance;
 599            }
 600
 601            private void ValidateArgs(object[] args) {
 602                int start = 0;
 603
 604                if (_inst != null) {
 605                    start = 1;
 606                }
 607
 608
 609                // no need to validate self... the method should handle it.
 610                for (int i = start; i < args.Length + start; i++) {
 611                    PythonType dt = DynamicHelpers.GetPythonType(args[i - start]);
 612
 613                    PythonType expct = _expected[i] as PythonType;
 614                    if (expct == null) expct = ((OldClass)_expected[i]).TypeObject;
 615                    if (dt != _expected[i] && !dt.IsSubclassOf(expct)) {
 616                        throw PythonOps.AssertionError("argument {0} has bad value (got {1}, expected {2})", i, dt, _expected[i]);
 617                    }
 618                }
 619            }
 620
 621            #region ICallableWithCodeContext Members
 622            [SpecialName]
 623            public object Call(CodeContext context, params object[] args) {
 624                ValidateArgs(args);
 625
 626                if (_inst != null) {
 627                    return PythonOps.CallWithContext(context, _func, ArrayUtils.Insert(_inst, args));
 628                } else {
 629                    return PythonOps.CallWithContext(context, _func, args);
 630                }
 631            }
 632
 633            #endregion
 634
 635            internal override bool TryGetValue(CodeContext context, object instance, PythonType owner, out object value) {
 636                value = new RuntimeArgChecker(instance, _func, _expected);
 637                return true;
 638            }
 639
 640            internal override bool GetAlwaysSucceeds {
 641                get {
 642                    return true;
 643                }
 644            }
 645
 646            #region IFancyCallable Members
 647            [SpecialName]
 648            public object Call(CodeContext context, [ParamDictionary]IDictionary<object, object> dict, params object[] args) {
 649                ValidateArgs(args);
 650
 651                if (_inst != null) {
 652                    return PythonCalls.CallWithKeywordArgs(context, _func, ArrayUtils.Insert(_inst, args), dict);
 653                } else {
 654                    return PythonCalls.CallWithKeywordArgs(context, _func, args, dict);
 655                }
 656            }
 657
 658            #endregion
 659        }
 660
 661        /// <summary>
 662        /// Decorator for verifying the return type of functions.
 663        /// </summary>
 664        public class ReturnChecker {
 665            public object retType;
 666
 667            public ReturnChecker(object returnType) {
 668                retType = returnType;
 669            }
 670
 671            #region ICallableWithCodeContext Members
 672            [SpecialName]
 673            public object Call(CodeContext context, object func) {
 674                // expect only to receive the function we'll call here.
 675                return new RuntimeReturnChecker(func, retType);
 676            }
 677
 678            #endregion
 679        }
 680
 681        /// <summary>
 682        /// Returned value when using clr.returns/ReturnChecker.  Calls the original function and
 683        /// validates the return type is of a specified type.
 684        /// </summary>
 685        public class RuntimeReturnChecker : PythonTypeSlot {
 686            private object _retType;
 687            private object _func;
 688            private object _inst;
 689
 690            public RuntimeReturnChecker(object function, object expectedReturn) {
 691                _retType = expectedReturn;
 692                _func = function;
 693            }
 694
 695            public RuntimeReturnChecker(object instance, object function, object expectedReturn)
 696                : this(function, expectedReturn) {
 697                _inst = instance;
 698            }
 699
 700            private void ValidateReturn(object ret) {
 701                // we return void...
 702                if (ret == null && _retType == null) return;
 703
 704                PythonType dt = DynamicHelpers.GetPythonType(ret);
 705                if (dt != _retType) {
 706                    PythonType expct = _retType as PythonType;
 707                    if (expct == null) expct = ((OldClass)_retType).TypeObject;
 708
 709                    if (!dt.IsSubclassOf(expct))
 710                        throw PythonOps.AssertionError("bad return value returned (expected {0}, got {1})", _retType, dt);
 711                }
 712            }
 713
 714            #region ICallableWithCodeContext Members
 715            [SpecialName]
 716            public object Call(CodeContext context, params object[] args) {
 717                object ret;
 718                if (_inst != null) {
 719                    ret = PythonOps.CallWithContext(context, _func, ArrayUtils.Insert(_inst, args));
 720                } else {
 721                    ret = PythonOps.CallWithContext(context, _func, args);
 722                }
 723                ValidateReturn(ret);
 724                return ret;
 725            }
 726
 727            #endregion
 728
 729            #region IDescriptor Members
 730
 731            public object GetAttribute(object instance, object owner) {
 732                return new RuntimeReturnChecker(instance, _func, _retType);
 733            }
 734
 735
 736            #endregion
 737
 738            internal override bool TryGetValue(CodeContext context, object instance, PythonType owner, out object value) {
 739                value = GetAttribute(instance, owner);
 740                return true;
 741            }
 742
 743            internal override bool GetAlwaysSucceeds {
 744                get {
 745                    return true;
 746                }
 747            }
 748
 749            #region IFancyCallable Members
 750            [SpecialName]
 751            public object Call(CodeContext context, [ParamDictionary]IDictionary<object, object> dict, params object[] args) {
 752                object ret;
 753                if (_inst != null) {
 754                    ret = PythonCalls.CallWithKeywordArgs(context, _func, ArrayUtils.Insert(_inst, args), dict);
 755                } else {
 756                    return PythonCalls.CallWithKeywordArgs(context, _func, args, dict);
 757                }
 758                ValidateReturn(ret);
 759                return ret;
 760            }
 761
 762            #endregion
 763        }
 764
 765        /// <summary>
 766        /// returns the result of dir(o) as-if "import clr" has not been performed.
 767        /// </summary>
 768        public static List Dir(object o) {
 769            IList<object> ret = PythonOps.GetAttrNames(DefaultContext.Default, o);
 770            List lret = new List(ret);
 771            lret.sort(DefaultContext.Default);
 772            return lret;
 773        }
 774
 775        /// <summary>
 776        /// Returns the result of dir(o) as-if "import clr" has been performed.
 777        /// </summary>
 778        public static List DirClr(object o) {
 779            IList<object> ret = PythonOps.GetAttrNames(DefaultContext.DefaultCLS, o);
 780            List lret = new List(ret);
 781            lret.sort(DefaultContext.DefaultCLS);
 782            return lret;
 783        }
 784
 785        /// <summary>
 786        /// Attempts to convert the provided object to the specified type.  Conversions that 
 787        /// will be attempted include standard Python conversions as well as .NET implicit
 788        /// and explicit conversions.
 789        /// 
 790        /// If the conversion cannot be performed a TypeError will be raised.
 791        /// </summary>
 792        public static object Convert(CodeContext/*!*/ context, object o, Type toType) {
 793            return Converter.Convert(o, toType);
 794        }
 795
 796        /// <summary>
 797        /// Provides a helper for compiling a group of modules into a single assembly.  The assembly can later be
 798        /// reloaded using the clr.AddReference API.
 799        /// </summary>
 800        public static void CompileModules(CodeContext/*!*/ context, string/*!*/ assemblyName, [ParamDictionary]IDictionary<string, object> kwArgs, params string/*!*/[]/*!*/ filenames) {
 801            ContractUtils.RequiresNotNull(assemblyName, "assemblyName");
 802            ContractUtils.RequiresNotNullItems(filenames, "filenames");
 803
 804            PythonContext pc = PythonContext.GetContext(context);
 805
 806            for (int i = 0; i < filenames.Length; i++) {
 807                filenames[i] = Path.GetFullPath(filenames[i]);
 808            }
 809
 810            Dictionary<string, string> packageMap = BuildPackageMap(filenames);
 811
 812            List<SavableScriptCode> code = new List<SavableScriptCode>();
 813            foreach (string filename in filenames) {
 814                if (!pc.DomainManager.Platform.FileExists(filename)) {
 815                    throw PythonOps.IOError("Couldn't find file for compilation: {0}", filename);
 816                }
 817
 818                ScriptCode sc;
 819
 820                string modName;
 821                string dname = Path.GetDirectoryName(filename);
 822                string outFilename = "";
 823                if (Path.GetFileName(filename) == "__init__.py") {
 824                    // remove __init__.py to get package name
 825                    dname = Path.GetDirectoryName(dname);
 826                    if (String.IsNullOrEmpty(dname)) {
 827                        modName = Path.GetDirectoryName(filename);
 828                    } else {
 829                        modName = Path.GetFileNameWithoutExtension(Path.GetDirectoryName(filename));
 830                    }
 831                    outFilename = Path.DirectorySeparatorChar + "__init__.py";
 832                } else {
 833                    modName = Path.GetFileNameWithoutExtension(filename);
 834                }
 835                
 836                // see if we have a parent package, if so incorporate it into
 837                // our name
 838                string parentPackage;
 839                if (packageMap.TryGetValue(dname, out parentPackage)) {
 840                    modName = parentPackage + "." + modName;
 841                }
 842
 843                outFilename = modName.Replace('.', Path.DirectorySeparatorChar) + outFilename;
 844
 845                SourceUnit su = pc.CreateSourceUnit(
 846                    new FileStreamContentProvider(
 847                        context.LanguageContext.DomainManager.Platform,
 848                        filename
 849                    ),
 850                    outFilename,
 851                    pc.DefaultEncoding,
 852                    SourceCodeKind.File
 853                );
 854
 855                sc = PythonContext.GetContext(context).GetScriptCode(su, modName, ModuleOptions.Initialize, Compiler.CompilationMode.ToDisk);
 856
 857                code.Add((SavableScriptCode)sc);
 858            }
 859
 860            object mainModule;
 861            if (kwArgs != null && kwArgs.TryGetValue("mainModule", out mainModule)) {
 862                string strModule = mainModule as string;
 863                if (strModule != null) {
 864                    if (!pc.DomainManager.Platform.FileExists(strModule)) {
 865                        throw PythonOps.IOError("Couldn't find main file for compilation: {0}", strModule);
 866                    }
 867                    
 868                    SourceUnit su = pc.CreateFileUnit(strModule, pc.DefaultEncoding, SourceCodeKind.File);
 869                    code.Add((SavableScriptCode)PythonContext.GetContext(context).GetScriptCode(su, "__main__", ModuleOptions.Initialize, Compiler.CompilationMode.ToDisk));
 870                }
 871            }
 872
 873            SavableScriptCode.SaveToAssembly(assemblyName, code.ToArray());
 874        }
 875
 876        /// <summary>
 877        /// clr.CompileSubclassTypes(assemblyName, *typeDescription)
 878        /// 
 879        /// Provides a helper for creating an assembly which contains pre-generated .NET 
 880        /// base types for new-style types.
 881        /// 
 882        /// This assembly can then be AddReferenced or put sys.prefix\DLLs and the cached 
 883        /// types will be used instead of generating the types at runtime.
 884        /// 
 885        /// This function takes the name of the assembly to save to and then an arbitrary 
 886        /// number of parameters describing the types to be created.  Each of those
 887        /// parameter can either be a plain type or a sequence of base types.
 888        /// 
 889        /// clr.CompileSubclassTypes(object) -> create a base type for object
 890        /// clr.CompileSubclassTypes(object, str, System.Collections.ArrayList) -> create 
 891        ///     base  types for both object and ArrayList.
 892        ///     
 893        /// clr.CompileSubclassTypes(object, (object, IComparable)) -> create base types for 
 894        ///     object and an object which implements IComparable.
 895        /// 
 896        /// </summary>
 897        public static void CompileSubclassTypes(string/*!*/ assemblyName, params object[] newTypes) {
 898            if (assemblyName == null) {
 899                throw PythonOps.TypeError("CompileTypes expected str for assemblyName, got NoneType");
 900            }
 901
 902            var typesToCreate = new List<PythonTuple>();
 903            foreach (object o in newTypes) {
 904                if (o is PythonType) {
 905                    typesToCreate.Add(PythonTuple.MakeTuple(o));
 906                } else {
 907                    typesToCreate.Add(PythonTuple.Make(o));
 908                }
 909            }
 910
 911            NewTypeMaker.SaveNewTypes(assemblyName, typesToCreate);
 912        }
 913
 914        /// <summary>
 915        /// clr.GetSubclassedTypes() -> tuple
 916        /// 
 917        /// Returns a tuple of information about the types which have been subclassed. 
 918        /// 
 919        /// This tuple can be passed to clr.CompileSubclassTypes to cache these
 920        /// types on disk such as:
 921        /// 
 922        /// clr.CompileSubclassTypes('assembly', *clr.GetSubclassedTypes())
 923        /// </summary>
 924        public static PythonTuple GetSubclassedTypes() {
 925            List<object> res = new List<object>();
 926            
 927            foreach (NewTypeInfo info in NewTypeMaker._newTypes.Keys) {
 928                Type clrBaseType = info.BaseType;
 929                Type tempType = clrBaseType;
 930                while (tempType != null) {
 931                    if (tempType.IsGenericType && tempType.GetGenericTypeDefinition() == typeof(Extensible<>)) {
 932                        clrBaseType = tempType.GetGenericArguments()[0];
 933                        break;
 934                    }
 935                    tempType = tempType.BaseType;
 936                }
 937
 938                PythonType baseType = DynamicHelpers.GetPythonTypeFromType(clrBaseType);
 939                if (info.InterfaceTypes.Count == 0) {
 940                    res.Add(baseType);
 941                } else if (info.InterfaceTypes.Count > 0) {
 942                    PythonType[] types = new PythonType[info.InterfaceTypes.Count + 1];
 943                    types[0] = baseType;
 944                    for (int i = 0; i < info.InterfaceTypes.Count; i++) {
 945                        types[i + 1] = DynamicHelpers.GetPythonTypeFromType(info.InterfaceTypes[i]);
 946                    }
 947                    res.Add(PythonTuple.MakeTuple(types));
 948                }
 949            }
 950
 951            return PythonTuple.MakeTuple(res.ToArray());
 952        }
 953
 954        /// <summary>
 955        /// Provides a StreamContentProvider for a stream of content backed by a file on disk.
 956        /// </summary>
 957        [Serializable]
 958        internal sealed class FileStreamContentProvider : StreamContentProvider {
 959            private readonly string _path;
 960            private readonly PALHolder _pal;
 961
 962            internal string Path {
 963                get { return _path; }
 964            }
 965
 966            #region Construction
 967
 968            internal FileStreamContentProvider(PlatformAdaptationLayer manager, string path) {
 969                ContractUtils.RequiresNotNull(path, "path");
 970
 971                _path = path;
 972                _pal = new PALHolder(manager);
 973            }
 974
 975            #endregion
 976
 977            public override Stream GetStream() {
 978                return _pal.GetStream(Path);
 979            }
 980
 981            [Serializable]
 982            private class PALHolder
 983#if !SILVERLIGHT
 984 : MarshalByRefObject
 985#endif
 986 {
 987                [NonSerialized]
 988                private readonly PlatformAdaptationLayer _pal;
 989
 990                internal PALHolder(PlatformAdaptationLayer pal) {
 991                    _pal = pal;
 992                }
 993
 994                internal Stream GetStream(string path) {
 995                    return _pal.OpenInputFileStream(path);
 996                }
 997            }
 998        }
 999
1000        /// <summary>
1001        /// Goes through the list of files identifying the relationship between packages
1002        /// and subpackages.  Returns a dictionary with all of the package filenames (minus __init__.py)
1003        /// mapping to their full name.  For example given a structure:
1004        /// 
1005        /// C:\
1006        ///     someDir\
1007        ///         package\
1008        ///             __init__.py
1009        ///             a.py
1010        ///             b\
1011        ///                 __init.py
1012        ///                 c.py
1013        ///         
1014        /// Returns:
1015        ///     {r'C:\somedir\package' : 'package', r'C:\somedir\package\b', 'package.b'}
1016        ///     
1017        /// This can then be used for calculating the full module name of individual files
1018        /// and packages.  For example a's full name is "package.a" and c's full name is
1019        /// "package.b.c".
1020        /// </summary>
1021        private static Dictionary<string/*!*/, string/*!*/>/*!*/ BuildPackageMap(string/*!*/[]/*!*/ filenames) {
1022            // modules which are the children of packages should have the __name__
1023            // package.subpackage.modulename, not just modulename.  So first
1024            // we need to get a list of all the modules...
1025            List<string> modules = new List<string>();
1026            foreach (string filename in filenames) {
1027                if (filename.EndsWith("__init__.py")) {
1028                    // this is a package
1029                    modules.Add(filename);
1030                }
1031            }
1032
1033            // next we need to understand the relationship between the packages so
1034            // if we have package.subpackage1 and package.subpackage2 we know
1035            // both of these are children of the package.  So sort the module names,
1036            // shortest name first...
1037            SortModules(modules);
1038
1039            // finally build up the package names for the dirs...
1040            Dictionary<string, string> packageMap = new Dictionary<string, string>();
1041            foreach (string packageName in modules) {
1042                string dirName = Path.GetDirectoryName(packageName);    // remove __init__.py
1043                string pkgName = String.Empty;
1044                string fullName = Path.GetFileName(Path.GetDirectoryName(packageName));
1045
1046                if (packageMap.TryGetValue(Path.GetDirectoryName(dirName), out pkgName)) {   // remove directory name
1047                    fullName = pkgName + "." + fullName;
1048                }
1049
1050                packageMap[Path.GetDirectoryName(packageName)] = fullName;
1051            }
1052            return packageMap;
1053        }
1054
1055        private static void SortModules(List<string> modules) {
1056            modules.Sort((string x, string y) => x.Length - y.Length);
1057        }
1058
1059        /// <summary>
1060        /// Returns a list of profile data. The values are tuples of Profiler.Data objects
1061        /// 
1062        /// All times are expressed in the same unit of measure as DateTime.Ticks
1063        /// </summary>
1064        public static PythonTuple GetProfilerData(CodeContext/*!*/ context, [DefaultParameterValue(false)]bool includeUnused) {
1065            return new PythonTuple(Profiler.GetProfiler(PythonContext.GetContext(context)).GetProfile(includeUnused));
1066        }
1067
1068        /// <summary>
1069        /// Resets all profiler counters back to zero
1070        /// </summary>
1071        public static void ClearProfilerData(CodeContext/*!*/ context) {
1072            Profiler.GetProfiler(PythonContext.GetContext(context)).Reset();
1073        }
1074
1075        /// <summary>
1076        /// Enable or disable profiling for the current ScriptEngine.  This will only affect code
1077        /// that is compiled after the setting is changed; previously-compiled code will retain
1078        /// whatever setting was active when the code was originally compiled.
1079        /// 
1080        /// The easiest way to recompile a module is to reload() it.
1081        /// </summary>
1082        public static void EnableProfiler(CodeContext/*!*/ context, bool enable) {
1083            var pc = PythonContext.GetContext(context);
1084            var po = pc.Options as PythonOptions;
1085            po.EnableProfiler = enable;
1086        }
1087
1088#if !SILVERLIGHT
1089        /// <summary>
1090        /// Serializes data using the .NET serialization formatter for complex
1091        /// types.  Returns a tuple identifying the serialization format and the serialized 
1092        /// data which can be fed back into clr.Deserialize.
1093        /// 
1094        /// Current serialization formats include custom formats for primitive .NET
1095        /// types which aren't already recognized as tuples.  None is used to indicate
1096        /// that the Binary .NET formatter is used.
1097        /// </summary>
1098        public static PythonTuple/*!*/ Serialize(object self) {
1099            if (self == null) {
1100                return PythonTuple.MakeTuple(null, String.Empty);
1101            }
1102
1103            string data, format;
1104            switch (Type.GetTypeCode(CompilerHelpers.GetType(self))) {
1105                // for the primitive non-python types just do a simple
1106                // serialization
1107                case TypeCode.Byte:
1108                case TypeCode.Char:
1109                case TypeCode.DBNull:
1110                case TypeCode.Decimal:
1111                case TypeCode.Int16:
1112                case TypeCode.Int64:
1113                case TypeCode.SByte:
1114                case TypeCode.Single:
1115                case TypeCode.UInt16:
1116                case TypeCode.UInt32:
1117                case TypeCode.UInt64:
1118                    data = self.ToString();
1119                    format = CompilerHelpers.GetType(self).FullName;
1120                    break;
1121                default:
1122                    // something more complex, let the binary formatter handle it                    
1123                    BinaryFormatter bf = new BinaryFormatter();
1124                    MemoryStream stream = new MemoryStream();
1125                    bf.Serialize(stream, self);
1126                    data = stream.ToArray().MakeString();
1127                    format = null;
1128                    break;
1129            }
1130
1131            return PythonTuple.MakeTuple(format, data);
1132        }
1133
1134        /// <summary>
1135        /// Deserializes the result of a Serialize call.  This can be used to perform serialization
1136        /// for .NET types which are serializable.  This method is the callable object provided
1137        /// from __reduce_ex__ for .serializable .NET types.
1138        /// 
1139        /// The first parameter indicates the serialization format and is the first tuple element
1140        /// returned from the Serialize call.  
1141        /// 
1142        /// The second parameter is the serialized data.
1143        /// </summary>
1144        public static object Deserialize(string serializationFormat, [NotNull]string/*!*/ data) {
1145            if (serializationFormat != null) {
1146                switch (serializationFormat) {
1147                    case "System.Byte": return Byte.Parse(data);
1148                    case "System.Char": return Char.Parse(data);
1149                    case "System.DBNull": return DBNull.Value;
1150                    case "System.Decimal": return Decimal.Parse(data);
1151                    case "System.Int16": return Int16.Parse(data);
1152                    case "System.Int64": return Int64.Parse(data);
1153                    case "System.SByte": return SByte.Parse(data);
1154                    case "System.Single": return Single.Parse(data);
1155                    case "System.UInt16": return UInt16.Parse(data);
1156                    case "System.UInt32": return UInt32.Parse(data);
1157                    case "System.UInt64": return UInt64.Parse(data);
1158                    default:
1159                        throw PythonOps.ValueError("unknown serialization format: {0}", serializationFormat);
1160                }
1161            } else if (String.IsNullOrEmpty(

Large files files are truncated, but you can click here to view the full file