PageRenderTime 99ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/IronPython/IronPython/Runtime/Importer.cs

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