PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/DICK.B1/IronPython/Runtime/Importer.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 930 lines | 656 code | 138 blank | 136 comment | 224 complexity | 5627d04541e59fdfa465c6cf1798ccbf MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.IO;
  20. using System.Reflection;
  21. using System.Dynamic;
  22. using Microsoft.Scripting.Runtime;
  23. using Microsoft.Scripting.Utils;
  24. using System.Text;
  25. using Microsoft.Scripting;
  26. using Microsoft.Scripting.Actions;
  27. using IronPython.Modules;
  28. using IronPython.Runtime.Exceptions;
  29. using IronPython.Runtime.Operations;
  30. using IronPython.Runtime.Types;
  31. using IronPython.Runtime.Binding;
  32. namespace IronPython.Runtime {
  33. /// <summary>
  34. /// Importer class - used for importing modules. Used by Ops and __builtin__
  35. /// Singleton living on Python engine.
  36. /// </summary>
  37. public static class Importer {
  38. internal const string ModuleReloadMethod = "PerformModuleReload";
  39. #region Internal API Surface
  40. /// <summary>
  41. /// Gateway into importing ... called from Ops. Performs the initial import of
  42. /// a module and returns the module.
  43. /// </summary>
  44. public static object Import(CodeContext/*!*/ context, string fullName, PythonTuple from, int level) {
  45. PythonContext pc = PythonContext.GetContext(context);
  46. if (level == -1) {
  47. // no specific level provided, call the 4 param version so legacy code continues to work
  48. return pc.OldImportSite.Target(
  49. pc.OldImportSite,
  50. context,
  51. FindImportFunction(context),
  52. fullName,
  53. Builtin.globals(context),
  54. context.Dict,
  55. from
  56. );
  57. }
  58. // relative import or absolute import, in other words:
  59. //
  60. // from . import xyz
  61. // or
  62. // from __future__ import absolute_import
  63. return pc.ImportSite.Target(
  64. pc.ImportSite,
  65. context,
  66. FindImportFunction(context),
  67. fullName,
  68. Builtin.globals(context),
  69. context.Dict,
  70. from,
  71. level
  72. );
  73. }
  74. /// <summary>
  75. /// Gateway into importing ... called from Ops. This is called after
  76. /// importing the module and is used to return individual items from
  77. /// the module. The outer modules dictionary is then updated with the
  78. /// result.
  79. /// </summary>
  80. public static object ImportFrom(CodeContext/*!*/ context, object from, string name) {
  81. PythonModule scope = from as PythonModule;
  82. PythonType pt;
  83. NamespaceTracker nt;
  84. if (scope != null) {
  85. object ret;
  86. if (scope.GetType() == typeof(PythonModule)) {
  87. if (scope.__dict__.TryGetValue(name, out ret)) {
  88. return ret;
  89. }
  90. } else {
  91. // subclass of module, it could have overridden __getattr__ or __getattribute__
  92. if (PythonOps.TryGetBoundAttr(context, scope, name, out ret)) {
  93. return ret;
  94. }
  95. }
  96. object path;
  97. List listPath;
  98. if (scope.__dict__._storage.TryGetPath(out path) && (listPath = path as List) != null) {
  99. return ImportNestedModule(context, scope, name, listPath);
  100. }
  101. } else if ((pt = from as PythonType) != null) {
  102. PythonTypeSlot pts;
  103. object res;
  104. if (pt.TryResolveSlot(context, name, out pts) &&
  105. pts.TryGetValue(context, null, pt, out res)) {
  106. return res;
  107. }
  108. } else if ((nt = from as NamespaceTracker) != null) {
  109. object res = NamespaceTrackerOps.GetCustomMember(context, nt, name);
  110. if (res != OperationFailed.Value) {
  111. return res;
  112. }
  113. } else {
  114. // This is too lax, for example it allows from module.class import member
  115. object ret;
  116. if (PythonOps.TryGetBoundAttr(context, from, name, out ret)) {
  117. return ret;
  118. }
  119. }
  120. throw PythonOps.ImportError("Cannot import name {0}", name);
  121. }
  122. private static object ImportModuleFrom(CodeContext/*!*/ context, object from, string name) {
  123. PythonModule scope = from as PythonModule;
  124. if (scope != null) {
  125. object path;
  126. List listPath;
  127. if (scope.__dict__._storage.TryGetPath(out path) && (listPath = path as List) != null) {
  128. return ImportNestedModule(context, scope, name, listPath);
  129. }
  130. }
  131. NamespaceTracker ns = from as NamespaceTracker;
  132. if (ns != null) {
  133. object val;
  134. if (ns.TryGetValue(SymbolTable.StringToId(name), out val)) {
  135. return MemberTrackerToPython(context, val);
  136. }
  137. }
  138. throw PythonOps.ImportError("No module named {0}", name);
  139. }
  140. /// <summary>
  141. /// Called by the __builtin__.__import__ functions (general importing) and ScriptEngine (for site.py)
  142. ///
  143. /// level indiciates whether to perform absolute or relative imports.
  144. /// -1 indicates both should be performed
  145. /// 0 indicates only absolute imports should be performed
  146. /// Positive numbers indicate the # of parent directories to search relative to the calling module
  147. /// </summary>
  148. public static object ImportModule(CodeContext/*!*/ context, object globals, string/*!*/ modName, bool bottom, int level) {
  149. if (modName.IndexOf(Path.DirectorySeparatorChar) != -1) {
  150. throw PythonOps.ImportError("Import by filename is not supported.", modName);
  151. }
  152. string package = null;
  153. object attribute;
  154. PythonDictionary pyGlobals = globals as PythonDictionary;
  155. if (pyGlobals != null) {
  156. if (pyGlobals._storage.TryGetPackage(out attribute)) {
  157. package = attribute as string;
  158. if (package == null && attribute != null) {
  159. throw PythonOps.ValueError("__package__ set to non-string");
  160. }
  161. } else {
  162. package = null;
  163. if (level > 0) {
  164. // explicit relative import, calculate and store __package__
  165. object pathAttr, nameAttr;
  166. if (pyGlobals._storage.TryGetName(out nameAttr) && nameAttr is string) {
  167. if (pyGlobals._storage.TryGetPath(out pathAttr)) {
  168. pyGlobals["__package__"] = nameAttr;
  169. } else {
  170. pyGlobals["__package__"] = ((string)nameAttr).rpartition(".")[0];
  171. }
  172. }
  173. }
  174. }
  175. }
  176. object newmod = null;
  177. string[] parts = modName.Split('.');
  178. string finalName = null;
  179. if (level != 0) {
  180. // try a relative import
  181. // if importing a.b.c, import "a" first and then import b.c from a
  182. string name; // name of the module we are to import in relation to the current module
  183. PythonModule parentModule;
  184. List path; // path to search
  185. if (TryGetNameAndPath(context, globals, parts[0], level, package, out name, out path, out parentModule)) {
  186. finalName = name;
  187. // import relative
  188. if (!TryGetExistingOrMetaPathModule(context, name, path, out newmod)) {
  189. newmod = ImportFromPath(context, parts[0], name, path);
  190. if (newmod != null && parentModule != null) {
  191. parentModule.__dict__[modName] = newmod;
  192. }
  193. } else if (parts.Length == 1) {
  194. // if we imported before having the assembly
  195. // loaded and then loaded the assembly we want
  196. // to make the assembly available now.
  197. if (newmod is NamespaceTracker) {
  198. context.ShowCls = true;
  199. }
  200. }
  201. }
  202. }
  203. if (level <= 0) {
  204. // try an absolute import
  205. if (newmod == null) {
  206. object parentPkg;
  207. if (!String.IsNullOrEmpty(package) && !PythonContext.GetContext(context).SystemStateModules.TryGetValue(package, out parentPkg)) {
  208. PythonModule warnModule = new PythonModule();
  209. warnModule.__dict__["__file__"] = package;
  210. warnModule.__dict__["__name__"] = package;
  211. ModuleContext modContext = new ModuleContext(warnModule.__dict__, context.LanguageContext);
  212. PythonOps.Warn(
  213. modContext.GlobalContext,
  214. PythonExceptions.RuntimeWarning,
  215. "Parent module '{0}' not found while handling absolute import",
  216. package);
  217. }
  218. newmod = ImportTopAbsolute(context, parts[0]);
  219. finalName = parts[0];
  220. if (newmod == null) {
  221. return null;
  222. }
  223. }
  224. }
  225. // now import the a.b.c etc. a needs to be included here
  226. // because the process of importing could have modified
  227. // sys.modules.
  228. object next = newmod;
  229. string curName = null;
  230. for (int i = 0; i < parts.Length; i++) {
  231. curName = i == 0 ? finalName : curName + "." + parts[i];
  232. object tmpNext;
  233. if (TryGetExistingModule(context, curName, out tmpNext)) {
  234. next = tmpNext;
  235. if (i == 0) {
  236. // need to update newmod if we pulled it out of sys.modules
  237. // just in case we're in bottom mode.
  238. newmod = next;
  239. }
  240. } else if (i != 0) {
  241. // child module isn't loaded yet, import it.
  242. next = ImportModuleFrom(context, next, parts[i]);
  243. } else {
  244. // top-level module doesn't exist in sys.modules, probably
  245. // came from some weird meta path hook.
  246. newmod = next;
  247. }
  248. }
  249. return bottom ? next : newmod;
  250. }
  251. /// <summary>
  252. /// Interrogates the importing module for __name__ and __path__, which determine
  253. /// whether the imported module (whose name is 'name') is being imported as nested
  254. /// module (__path__ is present) or as sibling.
  255. ///
  256. /// For sibling import, the full name of the imported module is parent.sibling
  257. /// For nested import, the full name of the imported module is parent.module.nested
  258. /// where parent.module is the mod.__name__
  259. /// </summary>
  260. /// <param name="context"></param>
  261. /// <param name="globals">the globals dictionary</param>
  262. /// <param name="name">Name of the module to be imported</param>
  263. /// <param name="full">Output - full name of the module being imported</param>
  264. /// <param name="path">Path to use to search for "full"</param>
  265. /// <param name="level">the import level for relaive imports</param>
  266. /// <param name="parentMod">the parent module</param>
  267. /// <param name="package">the global __package__ value</param>
  268. /// <returns></returns>
  269. private static bool TryGetNameAndPath(CodeContext/*!*/ context, object globals, string name, int level, string package, out string full, out List path, out PythonModule parentMod) {
  270. Debug.Assert(level != 0); // shouldn't be here for absolute imports
  271. // Unless we can find enough information to perform relative import,
  272. // we are going to import the module whose name we got
  273. full = name;
  274. path = null;
  275. parentMod = null;
  276. // We need to get __name__ to find the name of the imported module.
  277. // If absent, fall back to absolute import
  278. object attribute;
  279. PythonDictionary pyGlobals = globals as PythonDictionary;
  280. if (pyGlobals == null || !pyGlobals._storage.TryGetName(out attribute)) {
  281. return false;
  282. }
  283. // And the __name__ needs to be string
  284. string modName = attribute as string;
  285. if (modName == null) {
  286. return false;
  287. }
  288. string pn;
  289. if (package == null) {
  290. // If the module has __path__ (and __path__ is list), nested module is being imported
  291. // otherwise, importing sibling to the importing module
  292. if (pyGlobals._storage.TryGetPath(out attribute) && (path = attribute as List) != null) {
  293. // found __path__, importing nested module. The actual name of the nested module
  294. // is the name of the mod plus the name of the imported module
  295. if (level == -1) {
  296. // absolute import of some module
  297. full = modName + "." + name;
  298. object parentModule;
  299. if (PythonContext.GetContext(context).SystemStateModules.TryGetValue(modName, out parentModule)) {
  300. parentMod = parentModule as PythonModule;
  301. }
  302. } else if (String.IsNullOrEmpty(name)) {
  303. // relative import of ancestor
  304. full = (StringOps.rsplit(modName, ".", level - 1)[0] as string);
  305. } else {
  306. // relative import of some ancestors child
  307. string parentName = (StringOps.rsplit(modName, ".", level - 1)[0] as string);
  308. full = parentName + "." + name;
  309. object parentModule;
  310. if (PythonContext.GetContext(context).SystemStateModules.TryGetValue(parentName, out parentModule)) {
  311. parentMod = parentModule as PythonModule;
  312. }
  313. }
  314. return true;
  315. }
  316. // importing sibling. The name of the imported module replaces
  317. // the last element in the importing module name
  318. string[] names = modName.Split('.');
  319. if (names.Length == 1) {
  320. // name doesn't include dot, only absolute import possible
  321. if (level > 0) {
  322. throw PythonOps.ValueError("Attempted relative import in non-package");
  323. }
  324. return false;
  325. }
  326. pn = GetParentPackageName(level, names);
  327. } else {
  328. // __package__ doesn't include module name, so level is - 1.
  329. pn = GetParentPackageName(level - 1, package.Split('.'));
  330. }
  331. path = GetParentPathAndModule(context, pn, out parentMod);
  332. if (path != null) {
  333. if (String.IsNullOrEmpty(name)) {
  334. full = pn;
  335. } else {
  336. full = pn + "." + name;
  337. }
  338. return true;
  339. }
  340. if (level > 0) {
  341. throw PythonOps.SystemError("Parent module '{0}' not loaded, cannot perform relative import", pn);
  342. }
  343. // not enough information - absolute import
  344. return false;
  345. }
  346. private static string GetParentPackageName(int level, string[] names) {
  347. StringBuilder parentName = new StringBuilder(names[0]);
  348. if (level < 0) level = 1;
  349. for (int i = 1; i < names.Length - level; i++) {
  350. parentName.Append('.');
  351. parentName.Append(names[i]);
  352. }
  353. return parentName.ToString();
  354. }
  355. public static object ReloadModule(CodeContext/*!*/ context, PythonModule/*!*/ module) {
  356. return ReloadModule(context, module, null);
  357. }
  358. internal static object ReloadModule(CodeContext/*!*/ context, PythonModule/*!*/ module, PythonFile file) {
  359. PythonContext pc = PythonContext.GetContext(context);
  360. // We created the module and it only contains Python code. If the user changes
  361. // __file__ we'll reload from that file.
  362. string fileName = module.GetFile() as string;
  363. // built-in module:
  364. if (fileName == null) {
  365. ReloadBuiltinModule(context, module);
  366. return module;
  367. }
  368. string name = module.GetName() as string;
  369. if (name != null) {
  370. List path = null;
  371. // find the parent module and get it's __path__ property
  372. int dotIndex = name.LastIndexOf('.');
  373. if (dotIndex != -1) {
  374. PythonModule parentModule;
  375. path = GetParentPathAndModule(context, name.Substring(0, dotIndex), out parentModule);
  376. }
  377. object reloaded;
  378. if (TryLoadMetaPathModule(context, module.GetName() as string, path, out reloaded) && reloaded != null) {
  379. return module;
  380. }
  381. List sysPath;
  382. if (PythonContext.GetContext(context).TryGetSystemPath(out sysPath)) {
  383. object ret = ImportFromPathHook(context, name, name, sysPath, null);
  384. if (ret != null) {
  385. return ret;
  386. }
  387. }
  388. }
  389. SourceUnit sourceUnit;
  390. if (file != null) {
  391. sourceUnit = pc.CreateSourceUnit(new PythonFileStreamContentProvider(file), fileName, file.Encoding, SourceCodeKind.File);
  392. } else {
  393. if (!pc.DomainManager.Platform.FileExists(fileName)) {
  394. throw PythonOps.SystemError("module source file not found");
  395. }
  396. sourceUnit = pc.CreateFileUnit(fileName, pc.DefaultEncoding, SourceCodeKind.File);
  397. }
  398. pc.GetScriptCode(sourceUnit, name, ModuleOptions.None, Compiler.CompilationMode.Lookup).Run(module.Scope);
  399. return module;
  400. }
  401. class PythonFileStreamContentProvider : StreamContentProvider {
  402. private readonly PythonFile _file;
  403. public PythonFileStreamContentProvider(PythonFile file) {
  404. _file = file;
  405. }
  406. public override Stream GetStream() {
  407. return _file._stream;
  408. }
  409. }
  410. /// <summary>
  411. /// Given the parent module name looks up the __path__ property.
  412. /// </summary>
  413. private static List GetParentPathAndModule(CodeContext/*!*/ context, string/*!*/ parentModuleName, out PythonModule parentModule) {
  414. List path = null;
  415. object parentModuleObj;
  416. parentModule = null;
  417. // Try lookup parent module in the sys.modules
  418. if (PythonContext.GetContext(context).SystemStateModules.TryGetValue(parentModuleName, out parentModuleObj)) {
  419. // see if it's a module
  420. parentModule = parentModuleObj as PythonModule;
  421. if (parentModule != null) {
  422. object objPath;
  423. // get its path as a List if it's there
  424. if (parentModule.__dict__._storage.TryGetPath(out objPath)) {
  425. path = objPath as List;
  426. }
  427. }
  428. }
  429. return path;
  430. }
  431. private static void ReloadBuiltinModule(CodeContext/*!*/ context, PythonModule/*!*/ module) {
  432. Assert.NotNull(module);
  433. Debug.Assert(module.GetName() is string, "Module is reloadable only if its name is a non-null string");
  434. Type type;
  435. string name = (string)module.GetName();
  436. PythonContext pc = PythonContext.GetContext(context);
  437. if (!pc.BuiltinModules.TryGetValue(name, out type)) {
  438. throw PythonOps.ImportError("no module named {0}", module.GetName());
  439. }
  440. // should be a built-in module which we can reload.
  441. Debug.Assert(((PythonDictionary)module.__dict__)._storage is ModuleDictionaryStorage);
  442. ((ModuleDictionaryStorage)module.__dict__._storage).Reload();
  443. }
  444. /// <summary>
  445. /// Trys to get an existing module and if that fails fall backs to searching
  446. /// </summary>
  447. private static bool TryGetExistingOrMetaPathModule(CodeContext/*!*/ context, string fullName, List path, out object ret) {
  448. if (TryGetExistingModule(context, fullName, out ret)) {
  449. return true;
  450. }
  451. return TryLoadMetaPathModule(context, fullName, path, out ret);
  452. }
  453. /// <summary>
  454. /// Attempts to load a module from sys.meta_path as defined in PEP 302.
  455. ///
  456. /// The meta_path provides a list of importer objects which can be used to load modules before
  457. /// searching sys.path but after searching built-in modules.
  458. /// </summary>
  459. private static bool TryLoadMetaPathModule(CodeContext/*!*/ context, string fullName, List path, out object ret) {
  460. List metaPath = PythonContext.GetContext(context).GetSystemStateValue("meta_path") as List;
  461. if (metaPath != null) {
  462. foreach (object importer in (IEnumerable)metaPath) {
  463. if (FindAndLoadModuleFromImporter(context, importer, fullName, path, out ret)) {
  464. return true;
  465. }
  466. }
  467. }
  468. ret = null;
  469. return false;
  470. }
  471. /// <summary>
  472. /// Given a user defined importer object as defined in PEP 302 tries to load a module.
  473. ///
  474. /// First the find_module(fullName, path) is invoked to get a loader, then load_module(fullName) is invoked
  475. /// </summary>
  476. private static bool FindAndLoadModuleFromImporter(CodeContext/*!*/ context, object importer, string fullName, List path, out object ret) {
  477. object find_module = PythonOps.GetBoundAttr(context, importer, "find_module");
  478. PythonContext pycontext = PythonContext.GetContext(context);
  479. object loader = pycontext.Call(context, find_module, fullName, path);
  480. if (loader != null) {
  481. object findMod = PythonOps.GetBoundAttr(context, loader, "load_module");
  482. ret = pycontext.Call(context, findMod, fullName);
  483. return ret != null;
  484. }
  485. ret = null;
  486. return false;
  487. }
  488. internal static bool TryGetExistingModule(CodeContext/*!*/ context, string/*!*/ fullName, out object ret) {
  489. // Python uses None/null as a key here to indicate a missing module
  490. if (PythonContext.GetContext(context).SystemStateModules.TryGetValue(fullName, out ret)) {
  491. return ret != null;
  492. }
  493. return false;
  494. }
  495. #endregion
  496. #region Private Implementation Details
  497. private static object ImportTopAbsolute(CodeContext/*!*/ context, string/*!*/ name) {
  498. object ret;
  499. if (TryGetExistingModule(context, name, out ret)) {
  500. if (IsReflected(ret)) {
  501. // Even though we found something in sys.modules, we need to check if a
  502. // clr.AddReference has invalidated it. So try ImportReflected again.
  503. ret = ImportReflected(context, name) ?? ret;
  504. }
  505. NamespaceTracker rp = ret as NamespaceTracker;
  506. if (rp != null || ret == PythonContext.GetContext(context).ClrModule) {
  507. context.ShowCls = true;
  508. }
  509. return ret;
  510. }
  511. if (TryLoadMetaPathModule(context, name, null, out ret)) {
  512. return ret;
  513. }
  514. ret = ImportBuiltin(context, name);
  515. if (ret != null) return ret;
  516. List path;
  517. if (PythonContext.GetContext(context).TryGetSystemPath(out path)) {
  518. ret = ImportFromPath(context, name, name, path);
  519. if (ret != null) return ret;
  520. }
  521. ret = ImportReflected(context, name);
  522. if (ret != null) return ret;
  523. return null;
  524. }
  525. private static bool TryGetNestedModule(CodeContext/*!*/ context, PythonModule/*!*/ scope, string/*!*/ name, out object nested) {
  526. Assert.NotNull(context, scope, name);
  527. if (scope.__dict__.TryGetValue(name, out nested)) {
  528. if (nested is PythonModule) return true;
  529. // This allows from System.Math import *
  530. PythonType dt = nested as PythonType;
  531. if (dt != null && dt.IsSystemType) {
  532. return true;
  533. }
  534. }
  535. return false;
  536. }
  537. private static object ImportNestedModule(CodeContext/*!*/ context, PythonModule/*!*/ module, string name, List/*!*/ path) {
  538. object ret;
  539. string fullName = CreateFullName(module.GetName() as string, name);
  540. if (TryGetExistingOrMetaPathModule(context, fullName, path, out ret)) {
  541. module.__dict__[name] = ret;
  542. return ret;
  543. }
  544. if (TryGetNestedModule(context, module, name, out ret)) {
  545. return ret;
  546. }
  547. ImportFromPath(context, name, fullName, path);
  548. object importedModule;
  549. if (PythonContext.GetContext(context).SystemStateModules.TryGetValue(fullName, out importedModule)) {
  550. module.__dict__[name] = importedModule;
  551. return importedModule;
  552. }
  553. throw PythonOps.ImportError("cannot import {0} from {1}", name, module.GetName());
  554. }
  555. private static object FindImportFunction(CodeContext/*!*/ context) {
  556. PythonDictionary builtins = context.GetBuiltinsDict() ?? PythonContext.GetContext(context).BuiltinModuleDict;
  557. object import;
  558. if (builtins._storage.TryGetImport(out import)) {
  559. return import;
  560. }
  561. throw PythonOps.ImportError("cannot find __import__");
  562. }
  563. internal static object ImportBuiltin(CodeContext/*!*/ context, string/*!*/ name) {
  564. Assert.NotNull(context, name);
  565. PythonContext pc = PythonContext.GetContext(context);
  566. if (name == "sys") {
  567. return pc.SystemState;
  568. } else if (name == "clr") {
  569. context.ShowCls = true;
  570. pc.SystemStateModules["clr"] = pc.ClrModule;
  571. return pc.ClrModule;
  572. }
  573. return pc.GetBuiltinModule(name);
  574. }
  575. private static object ImportReflected(CodeContext/*!*/ context, string/*!*/ name) {
  576. object ret;
  577. PythonContext pc = PythonContext.GetContext(context);
  578. if (!PythonOps.ScopeTryGetMember(context, pc.DomainManager.Globals, name, out ret) &&
  579. (ret = pc.TopNamespace.TryGetPackageAny(name)) == null) {
  580. ret = TryImportSourceFile(pc, name);
  581. }
  582. ret = MemberTrackerToPython(context, ret);
  583. if (ret != null) {
  584. PythonContext.GetContext(context).SystemStateModules[name] = ret;
  585. }
  586. return ret;
  587. }
  588. internal static object MemberTrackerToPython(CodeContext/*!*/ context, object ret) {
  589. MemberTracker res = ret as MemberTracker;
  590. if (res != null) {
  591. context.ShowCls = true;
  592. object realRes = res;
  593. switch (res.MemberType) {
  594. case TrackerTypes.Type: realRes = DynamicHelpers.GetPythonTypeFromType(((TypeTracker)res).Type); break;
  595. case TrackerTypes.Field: realRes = PythonTypeOps.GetReflectedField(((FieldTracker)res).Field); break;
  596. case TrackerTypes.Event: realRes = PythonTypeOps.GetReflectedEvent((EventTracker)res); break;
  597. case TrackerTypes.Method:
  598. MethodTracker mt = res as MethodTracker;
  599. realRes = PythonTypeOps.GetBuiltinFunction(mt.DeclaringType, mt.Name, new MemberInfo[] { mt.Method });
  600. break;
  601. }
  602. ret = realRes;
  603. }
  604. return ret;
  605. }
  606. internal static PythonModule TryImportSourceFile(PythonContext/*!*/ context, string/*!*/ name) {
  607. var sourceUnit = TryFindSourceFile(context, name);
  608. if (sourceUnit == null ||
  609. GetFullPathAndValidateCase(context, Path.Combine(Path.GetDirectoryName(sourceUnit.Path), name + Path.GetExtension(sourceUnit.Path)), false) == null) {
  610. return null;
  611. }
  612. var scope = ExecuteSourceUnit(context, sourceUnit);
  613. if (sourceUnit.LanguageContext != context) {
  614. // foreign language, we should publish in sys.modules too
  615. context.SystemStateModules[name] = scope;
  616. }
  617. PythonOps.ScopeSetMember(context.SharedContext, sourceUnit.LanguageContext.DomainManager.Globals, name, scope);
  618. return scope;
  619. }
  620. internal static PythonModule ExecuteSourceUnit(PythonContext context, SourceUnit/*!*/ sourceUnit) {
  621. ScriptCode compiledCode = sourceUnit.Compile();
  622. Scope scope = compiledCode.CreateScope();
  623. PythonModule res = ((PythonScopeExtension)context.EnsureScopeExtension(scope)).Module;
  624. compiledCode.Run(scope);
  625. return res;
  626. }
  627. internal static SourceUnit TryFindSourceFile(PythonContext/*!*/ context, string/*!*/ name) {
  628. List paths;
  629. if (!context.TryGetSystemPath(out paths)) {
  630. return null;
  631. }
  632. foreach (object dirObj in paths) {
  633. string directory = dirObj as string;
  634. if (directory == null) continue; // skip invalid entries
  635. string candidatePath = null;
  636. LanguageContext candidateLanguage = null;
  637. foreach (string extension in context.DomainManager.Configuration.GetFileExtensions()) {
  638. string fullPath;
  639. try {
  640. fullPath = Path.Combine(directory, name + extension);
  641. } catch (ArgumentException) {
  642. // skip invalid paths
  643. continue;
  644. }
  645. if (context.DomainManager.Platform.FileExists(fullPath)) {
  646. if (candidatePath != null) {
  647. throw PythonOps.ImportError(String.Format("Found multiple modules of the same name '{0}': '{1}' and '{2}'",
  648. name, candidatePath, fullPath));
  649. }
  650. candidatePath = fullPath;
  651. candidateLanguage = context.DomainManager.GetLanguageByExtension(extension);
  652. }
  653. }
  654. if (candidatePath != null) {
  655. return candidateLanguage.CreateFileUnit(candidatePath);
  656. }
  657. }
  658. return null;
  659. }
  660. private static bool IsReflected(object module) {
  661. // corresponds to the list of types that can be returned by ImportReflected
  662. return module is MemberTracker
  663. || module is PythonType
  664. || module is ReflectedEvent
  665. || module is ReflectedField
  666. || module is BuiltinFunction;
  667. }
  668. private static string CreateFullName(string/*!*/ baseName, string name) {
  669. if (baseName == null || baseName.Length == 0 || baseName == "__main__") {
  670. return name;
  671. }
  672. return baseName + "." + name;
  673. }
  674. #endregion
  675. private static object ImportFromPath(CodeContext/*!*/ context, string/*!*/ name, string/*!*/ fullName, List/*!*/ path) {
  676. return ImportFromPathHook(context, name, fullName, path, LoadFromDisk);
  677. }
  678. private static object ImportFromPathHook(CodeContext/*!*/ context, string/*!*/ name, string/*!*/ fullName, List/*!*/ path, Func<CodeContext, string, string, string, object> defaultLoader) {
  679. Assert.NotNull(context, name, fullName, path);
  680. IDictionary<object, object> importCache = PythonContext.GetContext(context).GetSystemStateValue("path_importer_cache") as IDictionary<object, object>;
  681. if (importCache == null) {
  682. return null;
  683. }
  684. foreach (object dirname in (IEnumerable)path) {
  685. string str = dirname as string;
  686. if (str != null || (Converter.TryConvertToString(dirname, out str) && str != null)) { // ignore non-string
  687. object importer;
  688. if (!importCache.TryGetValue(str, out importer)) {
  689. importCache[str] = importer = FindImporterForPath(context, str);
  690. }
  691. if (importer != null) {
  692. // user defined importer object, get the loader and use it.
  693. object ret;
  694. if (FindAndLoadModuleFromImporter(context, importer, fullName, null, out ret)) {
  695. return ret;
  696. }
  697. } else if (defaultLoader != null) {
  698. object res = defaultLoader(context, name, fullName, str);
  699. if (res != null) {
  700. return res;
  701. }
  702. }
  703. }
  704. }
  705. return null;
  706. }
  707. private static object LoadFromDisk(CodeContext context, string name, string fullName, string str) {
  708. // default behavior
  709. PythonModule module;
  710. string pathname = Path.Combine(str, name);
  711. module = LoadPackageFromSource(context, fullName, pathname);
  712. if (module != null) {
  713. return module;
  714. }
  715. string filename = pathname + ".py";
  716. module = LoadModuleFromSource(context, fullName, filename);
  717. if (module != null) {
  718. return module;
  719. }
  720. return null;
  721. }
  722. /// <summary>
  723. /// Finds a user defined importer for the given path or returns null if no importer
  724. /// handles this path.
  725. /// </summary>
  726. private static object FindImporterForPath(CodeContext/*!*/ context, string dirname) {
  727. List pathHooks = PythonContext.GetContext(context).GetSystemStateValue("path_hooks") as List;
  728. foreach (object hook in (IEnumerable)pathHooks) {
  729. try {
  730. object handler = PythonCalls.Call(context, hook, dirname);
  731. if (handler != null) {
  732. return handler;
  733. }
  734. } catch (ImportException) {
  735. // we can't handle the path
  736. ExceptionHelpers.DynamicStackFrames = null;
  737. }
  738. }
  739. #if !SILVERLIGHT // DirectoryExists isn't implemented on Silverlight
  740. if (!context.LanguageContext.DomainManager.Platform.DirectoryExists(dirname)) {
  741. return new PythonImport.NullImporter(dirname);
  742. }
  743. #endif
  744. return null;
  745. }
  746. private static PythonModule LoadModuleFromSource(CodeContext/*!*/ context, string/*!*/ name, string/*!*/ path) {
  747. Assert.NotNull(context, name, path);
  748. PythonContext pc = PythonContext.GetContext(context);
  749. string fullPath = GetFullPathAndValidateCase(pc, path, false);
  750. if (fullPath == null || !pc.DomainManager.Platform.FileExists(fullPath)) {
  751. return null;
  752. }
  753. SourceUnit sourceUnit = pc.CreateFileUnit(fullPath, pc.DefaultEncoding, SourceCodeKind.File);
  754. return LoadFromSourceUnit(context, sourceUnit, name, sourceUnit.Path);
  755. }
  756. private static string GetFullPathAndValidateCase(LanguageContext/*!*/ context, string path, bool isDir) {
  757. #if !SILVERLIGHT
  758. // check for a match in the case of the filename, unfortunately we can't do this
  759. // in Silverlight becauase there's no way to get the original filename.
  760. PlatformAdaptationLayer pal = context.DomainManager.Platform;
  761. string dir = Path.GetDirectoryName(path);
  762. if (!pal.DirectoryExists(dir)) {
  763. return null;
  764. }
  765. try {
  766. string file = Path.GetFileName(path);
  767. string[] files = pal.GetFileSystemEntries(dir, file, !isDir, isDir);
  768. if (files.Length != 1 || Path.GetFileName(files[0]) != file) {
  769. return null;
  770. }
  771. return Path.GetFullPath(files[0]);
  772. } catch (IOException) {
  773. return null;
  774. }
  775. #else
  776. return path;
  777. #endif
  778. }
  779. internal static PythonModule LoadPackageFromSource(CodeContext/*!*/ context, string/*!*/ name, string/*!*/ path) {
  780. Assert.NotNull(context, name, path);
  781. path = GetFullPathAndValidateCase(PythonContext.GetContext(context), path, true);
  782. if (path == null) {
  783. return null;
  784. }
  785. return LoadModuleFromSource(context, name, Path.Combine(path, "__init__.py"));
  786. }
  787. private static PythonModule/*!*/ LoadFromSourceUnit(CodeContext/*!*/ context, SourceUnit/*!*/ sourceCode, string/*!*/ name, string/*!*/ path) {
  788. Assert.NotNull(sourceCode, name, path);
  789. return PythonContext.GetContext(context).CompileModule(path, name, sourceCode, ModuleOptions.Initialize | ModuleOptions.Optimized);
  790. }
  791. }
  792. }