PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/Languages/IronPython/IronPython.Modules/nt.cs

http://github.com/IronLanguages/main
C# | 1866 lines | 1449 code | 301 blank | 116 comment | 269 complexity | 5d49ee9affc6c622e73809bd99dbec5b MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception

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. * ironpy@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.ComponentModel;
  19. using System.Diagnostics;
  20. using System.Globalization;
  21. using System.IO;
  22. using System.Linq;
  23. using System.Runtime.InteropServices;
  24. using System.Security.Cryptography;
  25. using Microsoft.Scripting;
  26. using Microsoft.Scripting.Runtime;
  27. using Microsoft.Scripting.Utils;
  28. using IronPython.Runtime;
  29. using IronPython.Runtime.Exceptions;
  30. using IronPython.Runtime.Operations;
  31. using IronPython.Runtime.Types;
  32. #if FEATURE_NUMERICS
  33. using System.Numerics;
  34. #else
  35. using Microsoft.Scripting.Math;
  36. #endif
  37. #if NETCOREAPP1_0
  38. using Environment = System.FakeEnvironment;
  39. #endif
  40. [assembly: PythonModule("nt", typeof(IronPython.Modules.PythonNT))]
  41. namespace IronPython.Modules {
  42. public static class PythonNT {
  43. public const string __doc__ = "Provides low-level operationg system access for files, the environment, etc...";
  44. #if FEATURE_PROCESS
  45. private static Dictionary<int, Process> _processToIdMapping = new Dictionary<int, Process>();
  46. private static List<int> _freeProcessIds = new List<int>();
  47. private static int _processCount;
  48. #endif
  49. #region Public API Surface
  50. #if FEATURE_PROCESS
  51. public static void abort() {
  52. System.Environment.FailFast("IronPython os.abort");
  53. }
  54. #endif
  55. /// <summary>
  56. /// Checks for the specific permissions, provided by the mode parameter, are available for the provided path. Permissions can be:
  57. ///
  58. /// F_OK: Check to see if the file exists
  59. /// R_OK | W_OK | X_OK: Check for the specific permissions. Only W_OK is respected.
  60. /// </summary>
  61. public static bool access(CodeContext/*!*/ context, string path, int mode) {
  62. if (path == null) throw PythonOps.TypeError("expected string, got None");
  63. #if FEATURE_FILESYSTEM
  64. try {
  65. FileAttributes fa = File.GetAttributes(path);
  66. if (mode == F_OK) {
  67. return true;
  68. }
  69. // match the behavior of the VC C Runtime
  70. if ((fa & FileAttributes.Directory) != 0) {
  71. // directories have read & write access
  72. return true;
  73. }
  74. if ((fa & FileAttributes.ReadOnly) != 0 && (mode & W_OK) != 0) {
  75. // want to write but file is read-only
  76. return false;
  77. }
  78. return true;
  79. } catch(ArgumentException) {
  80. } catch(PathTooLongException) {
  81. } catch(NotSupportedException) {
  82. } catch(FileNotFoundException) {
  83. } catch(DirectoryNotFoundException) {
  84. } catch(IOException) {
  85. } catch(UnauthorizedAccessException) {
  86. }
  87. return false;
  88. #else
  89. throw new NotImplementedException();
  90. #endif
  91. }
  92. #if FEATURE_FILESYSTEM
  93. public static void chdir([NotNull]string path) {
  94. if (String.IsNullOrEmpty(path)) {
  95. throw PythonExceptions.CreateThrowable(WindowsError, PythonExceptions._WindowsError.ERROR_INVALID_NAME, "Path cannot be an empty string");
  96. }
  97. try {
  98. Directory.SetCurrentDirectory(path);
  99. } catch (Exception e) {
  100. throw ToPythonException(e, path);
  101. }
  102. }
  103. public static void chmod(string path, int mode) {
  104. try {
  105. FileInfo fi = new FileInfo(path);
  106. if ((mode & S_IWRITE) != 0) {
  107. fi.Attributes &= ~(FileAttributes.ReadOnly);
  108. } else {
  109. fi.Attributes |= FileAttributes.ReadOnly;
  110. }
  111. } catch (Exception e) {
  112. throw ToPythonException(e, path);
  113. }
  114. }
  115. #endif
  116. public static void close(CodeContext/*!*/ context, int fd) {
  117. PythonContext pythonContext = PythonContext.GetContext(context);
  118. PythonFileManager fileManager = pythonContext.FileManager;
  119. PythonFile file;
  120. if (fileManager.TryGetFileFromId(pythonContext, fd, out file)) {
  121. fileManager.CloseIfLast(fd, file);
  122. } else {
  123. Stream stream = fileManager.GetObjectFromId(fd) as Stream;
  124. if (stream == null) {
  125. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  126. }
  127. fileManager.CloseIfLast(fd, stream);
  128. }
  129. }
  130. public static void closerange(CodeContext/*!*/ context, int fd_low, int fd_high) {
  131. for (var fd = fd_low; fd <= fd_high; fd++) {
  132. try {
  133. close(context, fd);
  134. } catch (OSException) {
  135. // ignore errors on close
  136. }
  137. }
  138. }
  139. private static bool IsValidFd(CodeContext/*!*/ context, int fd) {
  140. PythonContext pythonContext = PythonContext.GetContext(context);
  141. PythonFile file;
  142. if (pythonContext.FileManager.TryGetFileFromId(pythonContext, fd, out file)) {
  143. return true;
  144. }
  145. Object o;
  146. if (pythonContext.FileManager.TryGetObjectFromId(pythonContext, fd, out o)) {
  147. var stream = o as Stream;
  148. if (stream != null) {
  149. return true;
  150. }
  151. }
  152. return false;
  153. }
  154. public static int dup(CodeContext/*!*/ context, int fd) {
  155. PythonContext pythonContext = PythonContext.GetContext(context);
  156. PythonFile file;
  157. if (pythonContext.FileManager.TryGetFileFromId(pythonContext, fd, out file)) {
  158. return pythonContext.FileManager.AddToStrongMapping(file);
  159. } else {
  160. Stream stream = pythonContext.FileManager.GetObjectFromId(fd) as Stream;
  161. if (stream == null) {
  162. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  163. }
  164. return pythonContext.FileManager.AddToStrongMapping(stream);
  165. }
  166. }
  167. public static int dup2(CodeContext/*!*/ context, int fd, int fd2) {
  168. PythonContext pythonContext = PythonContext.GetContext(context);
  169. PythonFile file;
  170. if (!IsValidFd(context, fd)) {
  171. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  172. }
  173. if (! pythonContext.FileManager.ValidateFdRange(fd2)) {
  174. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  175. }
  176. bool fd2Valid = IsValidFd(context, fd2);
  177. if (fd == fd2) {
  178. if (fd2Valid) {
  179. return fd2;
  180. }
  181. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  182. }
  183. if (fd2Valid) {
  184. close(context, fd2);
  185. }
  186. if (pythonContext.FileManager.TryGetFileFromId(pythonContext, fd, out file)) {
  187. return pythonContext.FileManager.AddToStrongMapping(file, fd2);
  188. }
  189. var stream = pythonContext.FileManager.GetObjectFromId(fd) as Stream;
  190. if (stream == null) {
  191. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  192. }
  193. return pythonContext.FileManager.AddToStrongMapping(stream, fd2);
  194. }
  195. #if FEATURE_PROCESS
  196. /// <summary>
  197. /// single instance of environment dictionary is shared between multiple runtimes because the environment
  198. /// is shared by multiple runtimes.
  199. /// </summary>
  200. public static readonly object environ = new PythonDictionary(new EnvironmentDictionaryStorage());
  201. #endif
  202. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
  203. public static readonly PythonType error = Builtin.OSError;
  204. public static void _exit(CodeContext/*!*/ context, int code) {
  205. PythonContext.GetContext(context).DomainManager.Platform.TerminateScriptExecution(code);
  206. }
  207. public static object fdopen(CodeContext/*!*/ context, int fd) {
  208. return fdopen(context, fd, "r");
  209. }
  210. public static object fdopen(CodeContext/*!*/ context, int fd, string mode) {
  211. return fdopen(context, fd, mode, 0);
  212. }
  213. public static object fdopen(CodeContext/*!*/ context, int fd, string mode, int bufsize) {
  214. // check for a valid file mode...
  215. PythonFile.ValidateMode(mode);
  216. PythonContext pythonContext = PythonContext.GetContext(context);
  217. PythonFile pf;
  218. if (pythonContext.FileManager.TryGetFileFromId(pythonContext, fd, out pf)) {
  219. return pf;
  220. }
  221. Stream stream = pythonContext.FileManager.GetObjectFromId(fd) as Stream;
  222. if (stream == null) {
  223. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  224. }
  225. return PythonFile.Create(context, stream, stream.ToString(), mode);
  226. }
  227. [LightThrowing]
  228. public static object fstat(CodeContext/*!*/ context, int fd) {
  229. PythonContext pythonContext = PythonContext.GetContext(context);
  230. PythonFile pf = pythonContext.FileManager.GetFileFromId(pythonContext, fd);
  231. if (pf.IsConsole) {
  232. return new stat_result(8192);
  233. }
  234. return lstat(pf.name);
  235. }
  236. public static void fsync(CodeContext context, int fd) {
  237. PythonContext pythonContext = PythonContext.GetContext(context);
  238. PythonFile pf = pythonContext.FileManager.GetFileFromId(pythonContext, fd);
  239. if (!pf.IsOutput) {
  240. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  241. }
  242. try {
  243. pf.FlushToDisk();
  244. } catch (Exception ex) {
  245. if (ex is ValueErrorException ||
  246. ex is IOException) {
  247. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 9, "Bad file descriptor");
  248. }
  249. throw;
  250. }
  251. }
  252. public static string getcwd(CodeContext/*!*/ context) {
  253. return context.LanguageContext.DomainManager.Platform.CurrentDirectory;
  254. }
  255. public static string getcwdu(CodeContext/*!*/ context) {
  256. return context.LanguageContext.DomainManager.Platform.CurrentDirectory;
  257. }
  258. public static string _getfullpathname(CodeContext/*!*/ context, [NotNull]string/*!*/ dir) {
  259. PlatformAdaptationLayer pal = context.LanguageContext.DomainManager.Platform;
  260. try {
  261. return pal.GetFullPath(dir);
  262. } catch (ArgumentException) {
  263. // .NET validates the path, CPython doesn't... so we replace invalid chars with
  264. // Char.Maxvalue, get the full path, and then replace the Char.Maxvalue's back w/
  265. // their original value.
  266. string newdir = dir;
  267. if (IsWindows()) {
  268. if (newdir.Length >= 2 && newdir[1] == ':' &&
  269. (newdir[0] < 'a' || newdir[0] > 'z') && (newdir[0] < 'A' || newdir[0] > 'Z')) {
  270. // invalid drive, .NET will reject this
  271. if (newdir.Length == 2) {
  272. return newdir + Path.DirectorySeparatorChar;
  273. } else if (newdir[2] == Path.DirectorySeparatorChar) {
  274. return newdir;
  275. } else {
  276. return newdir.Substring(0, 2) + Path.DirectorySeparatorChar + newdir.Substring(2);
  277. }
  278. }
  279. if (newdir.Length > 2 && newdir.IndexOf(':', 2) != -1) {
  280. // : is an invalid char if it's not in the 2nd position
  281. newdir = newdir.Substring(0, 2) + newdir.Substring(2).Replace(':', Char.MaxValue);
  282. }
  283. if (newdir.Length > 0 && newdir[0] == ':') {
  284. newdir = Char.MaxValue + newdir.Substring(1);
  285. }
  286. }
  287. foreach (char c in Path.GetInvalidPathChars()) {
  288. newdir = newdir.Replace(c, Char.MaxValue);
  289. }
  290. // walk backwards through the path replacing the same characters. We should have
  291. // only updated the directory leaving the filename which we're fixing.
  292. string res = pal.GetFullPath(newdir);
  293. int curDir = dir.Length;
  294. for (int curRes = res.Length - 1; curRes >= 0; curRes--) {
  295. if (res[curRes] == Char.MaxValue) {
  296. for (curDir--; curDir >= 0; curDir--) {
  297. if (newdir[curDir] == Char.MaxValue) {
  298. res = res.Substring(0, curRes) + dir[curDir] + res.Substring(curRes + 1);
  299. break;
  300. }
  301. }
  302. }
  303. }
  304. return res;
  305. }
  306. }
  307. private static bool IsWindows() {
  308. return Environment.OSVersion.Platform == PlatformID.Win32NT ||
  309. Environment.OSVersion.Platform == PlatformID.Win32S ||
  310. Environment.OSVersion.Platform == PlatformID.Win32Windows;
  311. }
  312. #if FEATURE_PROCESS
  313. public static int getpid() {
  314. return System.Diagnostics.Process.GetCurrentProcess().Id;
  315. }
  316. #endif
  317. public static List listdir(CodeContext/*!*/ context, [NotNull]string path) {
  318. if (path == String.Empty) {
  319. throw PythonOps.WindowsError("The system cannot find the path specified: '{0}'", path);
  320. }
  321. List ret = PythonOps.MakeList();
  322. try {
  323. addBase(context.LanguageContext.DomainManager.Platform.GetFileSystemEntries(path, "*"), ret);
  324. return ret;
  325. } catch (Exception e) {
  326. throw ToPythonException(e, path);
  327. }
  328. }
  329. public static void lseek(CodeContext context, int filedes, long offset, int whence) {
  330. PythonFile file = context.LanguageContext.FileManager.GetFileFromId(context.LanguageContext, filedes);
  331. file.seek(offset, whence);
  332. }
  333. /// <summary>
  334. /// lstat(path) -> stat result
  335. /// Like stat(path), but do not follow symbolic links.
  336. /// </summary>
  337. [LightThrowing]
  338. public static object lstat([BytesConversion]string path) {
  339. // TODO: detect links
  340. return stat(path);
  341. }
  342. #if FEATURE_UNIX && FEATURE_NATIVE
  343. [DllImport("libc")]
  344. private static extern int symlink(string source, string dest);
  345. public static void symlink(CodeContext context, string source, string link_name) {
  346. int result = symlink(source, link_name);
  347. if(result != 0) {
  348. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, 0, source, link_name);
  349. }
  350. }
  351. #endif
  352. #if FEATURE_FILESYSTEM
  353. public static void mkdir(string path) {
  354. if (Directory.Exists(path))
  355. throw DirectoryExists();
  356. try {
  357. Directory.CreateDirectory(path);
  358. } catch (Exception e) {
  359. throw ToPythonException(e, path);
  360. }
  361. }
  362. public static void mkdir(string path, int mode) {
  363. if (Directory.Exists(path)) throw DirectoryExists();
  364. // we ignore mode
  365. try {
  366. Directory.CreateDirectory(path);
  367. } catch (Exception e) {
  368. throw ToPythonException(e, path);
  369. }
  370. }
  371. public static object open(CodeContext/*!*/ context, string filename, int flag) {
  372. return open(context, filename, flag, 0777);
  373. }
  374. private const int DefaultBufferSize = 4096;
  375. public static object open(CodeContext/*!*/ context, string filename, int flag, int mode) {
  376. try {
  377. FileMode fileMode = FileModeFromFlags(flag);
  378. FileAccess access = FileAccessFromFlags(flag);
  379. FileOptions options = FileOptionsFromFlags(flag);
  380. Stream fs;
  381. if (Environment.OSVersion.Platform == PlatformID.Win32NT && (String.Compare(filename, "nul", true) == 0)) {
  382. fs = Stream.Null;
  383. } else if (access == FileAccess.Read && (fileMode == FileMode.CreateNew || fileMode == FileMode.Create || fileMode == FileMode.Append)) {
  384. // .NET doesn't allow Create/CreateNew w/ access == Read, so create the file, then close it, then
  385. // open it again w/ just read access.
  386. fs = new FileStream(filename, fileMode, FileAccess.Write, FileShare.None);
  387. fs.Dispose();
  388. fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, DefaultBufferSize, options);
  389. } else if (access == FileAccess.ReadWrite && fileMode == FileMode.Append) {
  390. fs = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, DefaultBufferSize, options);
  391. } else {
  392. fs = new FileStream(filename, fileMode, access, FileShare.ReadWrite, DefaultBufferSize, options);
  393. }
  394. string mode2;
  395. if (fs.CanRead && fs.CanWrite) mode2 = "w+";
  396. else if (fs.CanWrite) mode2 = "w";
  397. else mode2 = "r";
  398. if ((flag & O_BINARY) != 0) {
  399. mode2 += "b";
  400. }
  401. return PythonContext.GetContext(context).FileManager.AddToStrongMapping(PythonFile.Create(context, fs, filename, mode2));
  402. } catch (Exception e) {
  403. throw ToPythonException(e, filename);
  404. }
  405. }
  406. private static FileOptions FileOptionsFromFlags(int flag) {
  407. FileOptions res = FileOptions.None;
  408. if ((flag & O_TEMPORARY) != 0) {
  409. res |= FileOptions.DeleteOnClose;
  410. }
  411. if ((flag & O_RANDOM) != 0) {
  412. res |= FileOptions.RandomAccess;
  413. }
  414. if ((flag & O_SEQUENTIAL) != 0) {
  415. res |= FileOptions.SequentialScan;
  416. }
  417. return res;
  418. }
  419. #endif
  420. #if FEATURE_PIPES
  421. public static PythonTuple pipe(CodeContext context) {
  422. return PythonFile.CreatePipeAsFd(context);
  423. }
  424. #endif
  425. #if FEATURE_PROCESS
  426. public static PythonFile popen(CodeContext/*!*/ context, string command) {
  427. return popen(context, command, "r");
  428. }
  429. public static PythonFile popen(CodeContext/*!*/ context, string command, string mode) {
  430. return popen(context, command, mode, 4096);
  431. }
  432. public static PythonFile popen(CodeContext/*!*/ context, string command, string mode, int bufsize) {
  433. if (String.IsNullOrEmpty(mode)) mode = "r";
  434. ProcessStartInfo psi = GetProcessInfo(command, true);
  435. psi.CreateNoWindow = true; // ipyw shouldn't create a new console window
  436. Process p;
  437. PythonFile res;
  438. try {
  439. switch (mode) {
  440. case "r":
  441. psi.RedirectStandardOutput = true;
  442. p = Process.Start(psi);
  443. res = new POpenFile(context, command, p, p.StandardOutput.BaseStream, "r");
  444. break;
  445. case "w":
  446. psi.RedirectStandardInput = true;
  447. p = Process.Start(psi);
  448. res = new POpenFile(context, command, p, p.StandardInput.BaseStream, "w");
  449. break;
  450. default:
  451. throw PythonOps.ValueError("expected 'r' or 'w' for mode, got {0}", mode);
  452. }
  453. } catch (Exception e) {
  454. throw ToPythonException(e);
  455. }
  456. return res;
  457. }
  458. public static PythonTuple popen2(CodeContext/*!*/ context, string command) {
  459. return popen2(context, command, "t");
  460. }
  461. public static PythonTuple popen2(CodeContext/*!*/ context, string command, string mode) {
  462. return popen2(context, command, "t", 4096);
  463. }
  464. public static PythonTuple popen2(CodeContext/*!*/ context, string command, string mode, int bufsize) {
  465. if (String.IsNullOrEmpty(mode)) mode = "t";
  466. if (mode != "t" && mode != "b") throw PythonOps.ValueError("mode must be 't' or 'b' (default is t)");
  467. if (mode == "t") mode = String.Empty;
  468. try {
  469. ProcessStartInfo psi = GetProcessInfo(command, true);
  470. psi.RedirectStandardInput = true;
  471. psi.RedirectStandardOutput = true;
  472. psi.CreateNoWindow = true; // ipyw shouldn't create a new console window
  473. Process p = Process.Start(psi);
  474. return PythonTuple.MakeTuple(new POpenFile(context, command, p, p.StandardInput.BaseStream, "w" + mode),
  475. new POpenFile(context, command, p, p.StandardOutput.BaseStream, "r" + mode));
  476. } catch (Exception e) {
  477. throw ToPythonException(e);
  478. }
  479. }
  480. public static PythonTuple popen3(CodeContext/*!*/ context, string command) {
  481. return popen3(context, command, "t");
  482. }
  483. public static PythonTuple popen3(CodeContext/*!*/ context, string command, string mode) {
  484. return popen3(context, command, "t", 4096);
  485. }
  486. public static PythonTuple popen3(CodeContext/*!*/ context, string command, string mode, int bufsize) {
  487. if (String.IsNullOrEmpty(mode)) mode = "t";
  488. if (mode != "t" && mode != "b") throw PythonOps.ValueError("mode must be 't' or 'b' (default is t)");
  489. if (mode == "t") mode = String.Empty;
  490. try {
  491. ProcessStartInfo psi = GetProcessInfo(command, true);
  492. psi.RedirectStandardInput = true;
  493. psi.RedirectStandardOutput = true;
  494. psi.RedirectStandardError = true;
  495. psi.CreateNoWindow = true; // ipyw shouldn't create a new console window
  496. Process p = Process.Start(psi);
  497. return PythonTuple.MakeTuple(new POpenFile(context, command, p, p.StandardInput.BaseStream, "w" + mode),
  498. new POpenFile(context, command, p, p.StandardOutput.BaseStream, "r" + mode),
  499. new POpenFile(context, command, p, p.StandardError.BaseStream, "r+" + mode));
  500. } catch (Exception e) {
  501. throw ToPythonException(e);
  502. }
  503. }
  504. public static void putenv(string varname, string value) {
  505. try {
  506. System.Environment.SetEnvironmentVariable(varname, value);
  507. } catch (Exception e) {
  508. throw ToPythonException(e);
  509. }
  510. }
  511. #endif
  512. public static string read(CodeContext/*!*/ context, int fd, int buffersize) {
  513. if (buffersize < 0) {
  514. throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, PythonErrorNumber.EINVAL, "Invalid argument");
  515. }
  516. try {
  517. PythonContext pythonContext = PythonContext.GetContext(context);
  518. PythonFile pf = pythonContext.FileManager.GetFileFromId(pythonContext, fd);
  519. return pf.read(buffersize);
  520. } catch (Exception e) {
  521. throw ToPythonException(e);
  522. }
  523. }
  524. public static void rename(string src, string dst) {
  525. try {
  526. Directory.Move(src, dst);
  527. } catch (Exception e) {
  528. throw ToPythonException(e);
  529. }
  530. }
  531. public static void rmdir(string path) {
  532. try {
  533. Directory.Delete(path);
  534. } catch (Exception e) {
  535. throw ToPythonException(e, path);
  536. }
  537. }
  538. #if FEATURE_PROCESS
  539. /// <summary>
  540. /// spawns a new process.
  541. ///
  542. /// If mode is nt.P_WAIT then then the call blocks until the process exits and the return value
  543. /// is the exit code.
  544. ///
  545. /// Otherwise the call returns a handle to the process. The caller must then call nt.waitpid(pid, options)
  546. /// to free the handle and get the exit code of the process. Failure to call nt.waitpid will result
  547. /// in a handle leak.
  548. /// </summary>
  549. public static object spawnl(CodeContext/*!*/ context, int mode, string path, params object[] args) {
  550. return SpawnProcessImpl(context, MakeProcess(), mode, path, args);
  551. }
  552. /// <summary>
  553. /// spawns a new process.
  554. ///
  555. /// If mode is nt.P_WAIT then then the call blocks until the process exits and the return value
  556. /// is the exit code.
  557. ///
  558. /// Otherwise the call returns a handle to the process. The caller must then call nt.waitpid(pid, options)
  559. /// to free the handle and get the exit code of the process. Failure to call nt.waitpid will result
  560. /// in a handle leak.
  561. /// </summary>
  562. public static object spawnle(CodeContext/*!*/ context, int mode, string path, params object[] args) {
  563. if (args.Length < 1) {
  564. throw PythonOps.TypeError("spawnle() takes at least three arguments ({0} given)", 2 + args.Length);
  565. }
  566. object env = args[args.Length - 1];
  567. object[] slicedArgs = ArrayUtils.RemoveFirst(args);
  568. Process process = MakeProcess();
  569. #if NETSTANDARD
  570. SetEnvironment(process.StartInfo.Environment, env);
  571. #else
  572. SetEnvironment(process.StartInfo.EnvironmentVariables, env);
  573. #endif
  574. return SpawnProcessImpl(context, process, mode, path, slicedArgs);
  575. }
  576. /// <summary>
  577. /// spawns a new process.
  578. ///
  579. /// If mode is nt.P_WAIT then then the call blocks until the process exits and the return value
  580. /// is the exit code.
  581. ///
  582. /// Otherwise the call returns a handle to the process. The caller must then call nt.waitpid(pid, options)
  583. /// to free the handle and get the exit code of the process. Failure to call nt.waitpid will result
  584. /// in a handle leak.
  585. /// </summary>
  586. public static object spawnv(CodeContext/*!*/ context, int mode, string path, object args) {
  587. return SpawnProcessImpl(context, MakeProcess(), mode, path, args);
  588. }
  589. /// <summary>
  590. /// spawns a new process.
  591. ///
  592. /// If mode is nt.P_WAIT then then the call blocks until the process exits and the return value
  593. /// is the exit code.
  594. ///
  595. /// Otherwise the call returns a handle to the process. The caller must then call nt.waitpid(pid, options)
  596. /// to free the handle and get the exit code of the process. Failure to call nt.waitpid will result
  597. /// in a handle leak.
  598. /// </summary>
  599. public static object spawnve(CodeContext/*!*/ context, int mode, string path, object args, object env) {
  600. Process process = MakeProcess();
  601. #if NETSTANDARD
  602. SetEnvironment(process.StartInfo.Environment, env);
  603. #else
  604. SetEnvironment(process.StartInfo.EnvironmentVariables, env);
  605. #endif
  606. return SpawnProcessImpl(context, process, mode, path, args);
  607. }
  608. private static Process MakeProcess() {
  609. try {
  610. return new Process();
  611. } catch (Exception e) {
  612. throw ToPythonException(e);
  613. }
  614. }
  615. private static object SpawnProcessImpl(CodeContext/*!*/ context, Process process, int mode, string path, object args) {
  616. try {
  617. process.StartInfo.Arguments = ArgumentsToString(context, args);
  618. process.StartInfo.FileName = path;
  619. process.StartInfo.UseShellExecute = false;
  620. } catch (Exception e) {
  621. throw ToPythonException(e, path);
  622. }
  623. if (!process.Start()) {
  624. throw PythonOps.OSError("Cannot start process: {0}", path);
  625. }
  626. if (mode == P_WAIT) {
  627. process.WaitForExit();
  628. int exitCode = process.ExitCode;
  629. process.Dispose();
  630. return exitCode;
  631. }
  632. lock (_processToIdMapping) {
  633. int id;
  634. if (_freeProcessIds.Count > 0) {
  635. id = _freeProcessIds[_freeProcessIds.Count - 1];
  636. _freeProcessIds.RemoveAt(_freeProcessIds.Count - 1);
  637. } else {
  638. // process IDs are handles on CPython/Win32 so we match
  639. // that behavior and return something that is handle like. Handles
  640. // on NT are guaranteed to have the low 2 bits not set and users
  641. // could use these for their own purposes. We therefore match that
  642. // behavior here.
  643. _processCount += 4;
  644. id = _processCount;
  645. }
  646. _processToIdMapping[id] = process;
  647. return ScriptingRuntimeHelpers.Int32ToObject(id);
  648. }
  649. }
  650. /// <summary>
  651. /// Copy elements from a Python mapping of dict environment variables to a StringDictionary.
  652. /// </summary>
  653. #if NETSTANDARD
  654. private static void SetEnvironment(IDictionary<string, string> currentEnvironment, object newEnvironment) {
  655. #else
  656. private static void SetEnvironment(System.Collections.Specialized.StringDictionary currentEnvironment, object newEnvironment) {
  657. #endif
  658. PythonDictionary env = newEnvironment as PythonDictionary;
  659. if (env == null) {
  660. throw PythonOps.TypeError("env argument must be a dict");
  661. }
  662. currentEnvironment.Clear();
  663. string strKey, strValue;
  664. foreach (object key in env.keys()) {
  665. if (!Converter.TryConvertToString(key, out strKey)) {
  666. throw PythonOps.TypeError("env dict contains a non-string key");
  667. }
  668. if (!Converter.TryConvertToString(env[key], out strValue)) {
  669. throw PythonOps.TypeError("env dict contains a non-string value");
  670. }
  671. currentEnvironment[strKey] = strValue;
  672. }
  673. }
  674. #endif
  675. /// <summary>
  676. /// Convert a sequence of args to a string suitable for using to spawn a process.
  677. /// </summary>
  678. private static string ArgumentsToString(CodeContext/*!*/ context, object args) {
  679. IEnumerator argsEnumerator;
  680. System.Text.StringBuilder sb = null;
  681. if (!PythonOps.TryGetEnumerator(context, args, out argsEnumerator)) {
  682. throw PythonOps.TypeError("args parameter must be sequence, not {0}", DynamicHelpers.GetPythonType(args));
  683. }
  684. bool space = false;
  685. try {
  686. // skip the first element, which is the name of the command being run
  687. argsEnumerator.MoveNext();
  688. while (argsEnumerator.MoveNext()) {
  689. if (sb == null) sb = new System.Text.StringBuilder(); // lazy creation
  690. string strarg = PythonOps.ToString(argsEnumerator.Current);
  691. if (space) {
  692. sb.Append(' ');
  693. }
  694. if (strarg.IndexOf(' ') != -1) {
  695. sb.Append('"');
  696. // double quote any existing quotes
  697. sb.Append(strarg.Replace("\"", "\"\""));
  698. sb.Append('"');
  699. } else {
  700. sb.Append(strarg);
  701. }
  702. space = true;
  703. }
  704. } finally {
  705. IDisposable disposable = argsEnumerator as IDisposable;
  706. if (disposable != null) disposable.Dispose();
  707. }
  708. if (sb == null) return "";
  709. return sb.ToString();
  710. }
  711. #if FEATURE_PROCESS
  712. public static void startfile(string filename, [DefaultParameterValue("open")]string operation) {
  713. System.Diagnostics.Process process = new System.Diagnostics.Process();
  714. #if NETSTANDARD
  715. process.StartInfo.FileName = "cmd";
  716. process.StartInfo.Arguments = "/c " + filename;
  717. process.StartInfo.UseShellExecute = false;
  718. #else
  719. process.StartInfo.FileName = filename;
  720. process.StartInfo.UseShellExecute = true;
  721. process.StartInfo.Verb = operation;
  722. #endif
  723. try {
  724. #if NETSTANDARD
  725. if (!File.Exists(filename)) throw new Win32Exception("The system cannot find the file specified");
  726. #endif
  727. process.Start();
  728. } catch (Exception e) {
  729. throw ToPythonException(e, filename);
  730. }
  731. }
  732. #endif
  733. [PythonType, DontMapIEnumerableToIter]
  734. public class stat_result : IList, IList<object> {
  735. private readonly object _mode, _size, _atime, _mtime, _ctime, _st_atime, _st_mtime, _st_ctime, _ino, _dev, _nlink, _uid, _gid;
  736. public const int n_fields = 13;
  737. public const int n_sequence_fields = 10;
  738. public const int n_unnamed_fields = 3;
  739. internal stat_result(int mode) : this(mode, BigInteger.Zero, BigInteger.Zero, BigInteger.Zero, BigInteger.Zero) {
  740. _mode = mode;
  741. }
  742. internal stat_result(int mode, BigInteger size, BigInteger st_atime, BigInteger st_mtime, BigInteger st_ctime) {
  743. _mode = mode;
  744. _size = size;
  745. _st_atime = _atime = TryShrinkToInt(st_atime);
  746. _st_mtime = _mtime = TryShrinkToInt(st_mtime);
  747. _st_ctime = _ctime = TryShrinkToInt(st_ctime);
  748. _ino = _dev = _nlink = _uid = _gid = ScriptingRuntimeHelpers.Int32ToObject(0);
  749. }
  750. public stat_result(CodeContext/*!*/ context, IList statResult, [DefaultParameterValue(null)]PythonDictionary dict) {
  751. // dict is allowed by CPython's stat_result, but doesn't seem to do anything, so we ignore it here.
  752. if (statResult.Count < 10) {
  753. throw PythonOps.TypeError("stat_result() takes an at least 10-sequence ({0}-sequence given)", statResult.Count);
  754. }
  755. _mode = statResult[0];
  756. _ino = statResult[1];
  757. _dev = statResult[2];
  758. _nlink = statResult[3];
  759. _uid = statResult[4];
  760. _gid = statResult[5];
  761. _size = statResult[6];
  762. _atime = statResult[7];
  763. _mtime = statResult[8];
  764. _ctime = statResult[9];
  765. object dictTime;
  766. if (statResult.Count >= 11) {
  767. _st_atime = TryShrinkToInt(statResult[10]);
  768. } else if (TryGetDictValue(dict, "st_atime", out dictTime)) {
  769. _st_atime = dictTime;
  770. } else {
  771. _st_atime = TryShrinkToInt(_atime);
  772. }
  773. if (statResult.Count >= 12) {
  774. _st_mtime = TryShrinkToInt(statResult[11]);
  775. } else if (TryGetDictValue(dict, "st_mtime", out dictTime)) {
  776. _st_mtime = dictTime;
  777. } else {
  778. _st_mtime = TryShrinkToInt(_mtime);
  779. }
  780. if (statResult.Count >= 13) {
  781. _st_ctime = TryShrinkToInt(statResult[12]);
  782. } else if (TryGetDictValue(dict, "st_ctime", out dictTime)) {
  783. _st_ctime = dictTime;
  784. } else {
  785. _st_ctime = TryShrinkToInt(_ctime);
  786. }
  787. }
  788. private static bool TryGetDictValue(PythonDictionary dict, string name, out object dictTime) {
  789. if (dict != null && dict.TryGetValue(name, out dictTime)) {
  790. dictTime = TryShrinkToInt(dictTime);
  791. return true;
  792. }
  793. dictTime = null;
  794. return false;
  795. }
  796. private static object TryShrinkToInt(object value) {
  797. if (!(value is BigInteger)) {
  798. return value;
  799. }
  800. return BigIntegerOps.__int__((BigInteger)value);
  801. }
  802. public object st_atime {
  803. get {
  804. return _st_atime;
  805. }
  806. }
  807. public object st_ctime {
  808. get {
  809. return _st_ctime;
  810. }
  811. }
  812. public object st_mtime {
  813. get {
  814. return _st_mtime;
  815. }
  816. }
  817. public object st_dev {
  818. get {
  819. return TryShrinkToInt(_dev);
  820. }
  821. }
  822. public object st_gid {
  823. get {
  824. return _gid;
  825. }
  826. }
  827. public object st_ino {
  828. get {
  829. return _ino;
  830. }
  831. }
  832. public object st_mode {
  833. get {
  834. return TryShrinkToInt(_mode);
  835. }
  836. }
  837. public object st_nlink {
  838. get {
  839. return TryShrinkToInt(_nlink);
  840. }
  841. }
  842. public object st_size {
  843. get {
  844. return _size;
  845. }
  846. }
  847. public object st_uid {
  848. get {
  849. return _uid;
  850. }
  851. }
  852. public static PythonTuple operator +(stat_result stat, object tuple) {
  853. PythonTuple tupleObj = tuple as PythonTuple;
  854. if (tupleObj == null) {
  855. throw PythonOps.TypeError("can only concatenate tuple (not \"{0}\") to tuple", PythonTypeOps.GetName(tuple));
  856. }
  857. return stat.MakeTuple() + tupleObj;
  858. }
  859. public static bool operator >(stat_result stat, [NotNull]stat_result o) {
  860. return stat.MakeTuple() > PythonTuple.Make(o);
  861. }
  862. public static bool operator <(stat_result stat, [NotNull]stat_result o) {
  863. return stat.MakeTuple() < PythonTuple.Make(o);
  864. }
  865. public static bool operator >=(stat_result stat, [NotNull]stat_result o) {
  866. return stat.MakeTuple() >= PythonTuple.Make(o);
  867. }
  868. public static bool operator <=(stat_result stat, [NotNull]stat_result o) {
  869. return stat.MakeTuple() <= PythonTuple.Make(o);
  870. }
  871. public static bool operator >(stat_result stat, object o) {
  872. return true;
  873. }
  874. public static bool operator <(stat_result stat, object o) {
  875. return false;
  876. }
  877. public static bool operator >=(stat_result stat, object o) {
  878. return true;
  879. }
  880. public static bool operator <=(stat_result stat, object o) {
  881. return false;
  882. }
  883. public static PythonTuple operator *(stat_result stat, int size) {
  884. return stat.MakeTuple() * size;
  885. }
  886. public static PythonTuple operator *(int size, stat_result stat) {
  887. return stat.MakeTuple() * size;
  888. }
  889. public override string ToString() {
  890. return string.Format("nt.stat_result("
  891. + "st_mode={0}, "
  892. + "st_ino={1}, "
  893. + "st_dev={2}, "
  894. + "st_nlink={3}, "
  895. + "st_uid={4}, "
  896. + "st_gid={5}, "
  897. + "st_size={6}, "
  898. + "st_atime={7}, "
  899. + "st_mtime={8}, "
  900. + "st_ctime={9})", MakeTuple().ToArray());
  901. }
  902. public string/*!*/ __repr__() {
  903. return ToString();
  904. }
  905. public PythonTuple __reduce__() {
  906. PythonDictionary timeDict = new PythonDictionary(3);
  907. timeDict["st_atime"] = st_atime;
  908. timeDict["st_ctime"] = st_ctime;
  909. timeDict["st_mtime"] = st_mtime;
  910. return PythonTuple.MakeTuple(
  911. DynamicHelpers.GetPythonTypeFromType(typeof(stat_result)),
  912. PythonTuple.MakeTuple(MakeTuple(), timeDict)
  913. );
  914. }
  915. #region ISequence Members
  916. //public object AddSequence(object other) {
  917. // return MakeTuple().AddSequence(other);
  918. //}
  919. //public object MultiplySequence(object count) {
  920. // return MakeTuple().MultiplySequence(count);
  921. //}
  922. public object this[int index] {
  923. get {
  924. return MakeTuple()[index];
  925. }
  926. }
  927. public object this[Slice slice] {
  928. get {
  929. return MakeTuple()[slice];
  930. }
  931. }
  932. public object __getslice__(int start, int stop) {
  933. return MakeTuple().__getslice__(start, stop);
  934. }
  935. public int __len__() {
  936. return MakeTuple().__len__();
  937. }
  938. public bool __contains__(object item) {
  939. return ((ICollection<object>)MakeTuple()).Contains(item);
  940. }
  941. #endregion
  942. private PythonTuple MakeTuple() {
  943. return PythonTuple.MakeTuple(
  944. st_mode,
  945. st_ino,
  946. st_dev,
  947. st_nlink,
  948. st_uid,
  949. st_gid,
  950. st_size,
  951. _atime,
  952. _mtime,
  953. _ctime
  954. );
  955. }
  956. #region Object overrides
  957. public override bool Equals(object obj) {
  958. if (obj is stat_result) {
  959. return MakeTuple().Equals(((stat_result)obj).MakeTuple());
  960. } else {
  961. return MakeTuple().Equals(obj);
  962. }
  963. }
  964. public override int GetHashCode() {
  965. return MakeTuple().GetHashCode();
  966. }
  967. #endregion
  968. #region IList<object> Members
  969. int IList<object>.IndexOf(object item) {
  970. return MakeTuple().IndexOf(item);
  971. }
  972. void IList<object>.Insert(int index, object item) {
  973. throw new InvalidOperationException();
  974. }
  975. void IList<object>.RemoveAt(int index) {
  976. throw new InvalidOperationException();
  977. }
  978. object IList<object>.this[int index] {
  979. get {
  980. return MakeTuple()[index];
  981. }
  982. set {
  983. throw new InvalidOperationException();
  984. }
  985. }
  986. #endregion
  987. #region ICollection<object> Members
  988. void ICollection<object>.Add(object item) {
  989. throw new InvalidOperationException();
  990. }
  991. void ICollection<object>.Clear() {
  992. throw new InvalidOperationException();
  993. }
  994. bool ICollection<object>.Contains(object item) {
  995. return __contains__(item);
  996. }
  997. void ICollection<object>.CopyTo(object[] array, int arrayIndex) {
  998. throw new NotImplementedException();
  999. }
  1000. int ICollection<object>.Count {
  1001. get { return __len__(); }
  1002. }
  1003. bool ICollection<object>.IsReadOnly {
  1004. get { return true; }
  1005. }
  1006. bool ICollection<object>.Remove(object item) {
  1007. throw new InvalidOperationException();
  1008. }
  1009. #endregion
  1010. #region IEnumerable<object> Members
  1011. IEnumerator<object> IEnumerable<object>.GetEnumerator() {
  1012. foreach (object o in MakeTuple()) {
  1013. yield return o;
  1014. }
  1015. }
  1016. #endregion
  1017. #region IEnumerable Members
  1018. IEnumerator IEnumerable.GetEnumerator() {
  1019. foreach (object o in MakeTuple()) {
  1020. yield return o;
  1021. }
  1022. }
  1023. #endregion
  1024. #region IList Members
  1025. int IList.Add(object value) {
  1026. throw new InvalidOperationException();
  1027. }
  1028. void IList.Clear() {
  1029. throw new InvalidOperationException();
  1030. }
  1031. bool IList.Contains(object value) {
  1032. return __contains__(value);
  1033. }
  1034. int IList.IndexOf(object value) {
  1035. return MakeTuple().IndexOf(value);
  1036. }
  1037. void IList.Insert(int index, object value) {
  1038. throw new InvalidOperationException();
  1039. }
  1040. bool IList.IsFixedSize {
  1041. get { return true; }
  1042. }
  1043. bool IList.IsReadOnly {
  1044. get { return true; }
  1045. }
  1046. void IList.Remove(object value) {
  1047. throw new InvalidOperationException();
  1048. }
  1049. void IList.RemoveAt(int index) {
  1050. throw new InvalidOperationException();
  1051. }
  1052. object IList.this[int index] {
  1053. get {
  1054. return MakeTuple()[index];
  1055. }
  1056. set {
  1057. throw new InvalidOperationException();
  1058. }
  1059. }
  1060. #endregion
  1061. #region ICollection Members
  1062. void ICollection.CopyTo(Array array, int index) {
  1063. throw new NotImplementedException();
  1064. }
  1065. int ICollection.Count {
  1066. get { return __len__(); }
  1067. }
  1068. bool ICollection.IsSynchronized {
  1069. get { return false; }
  1070. }
  1071. object ICollection.SyncRoot {
  1072. get { return this; }
  1073. }
  1074. #endregion
  1075. }
  1076. private static bool HasExecutableExtension(string path) {
  1077. string extension = Path.GetExtension(path).ToLowerInvariant();
  1078. return (extension == ".exe" || extension == ".dll" || extension == ".com" || extension == ".bat");
  1079. }
  1080. [Documentation("stat(path) -> stat result\nGathers statistics about the specified file or directory")]
  1081. [LightThrowing]
  1082. public static object stat([BytesConversion]string path) {
  1083. if (path == null) {
  1084. return LightExceptions.Throw(PythonOps.TypeError("expected string, got NoneType"));
  1085. }
  1086. stat_result sr;
  1087. try {
  1088. FileInfo fi = new FileInfo(path);
  1089. int mode = 0;
  1090. long size;
  1091. if (Directory.Exists(path)) {
  1092. size = 0;
  1093. mode = 0x4000 | S_IEXEC;
  1094. } else if (File.Exists(path)) {
  1095. size = fi.Length;
  1096. mode = 0x8000;
  1097. if (HasExecutableExtension(path)) {
  1098. mode |= S_IEXEC;
  1099. }
  1100. } else {
  1101. return LightExceptions.Throw(PythonExceptions.CreateThrowable(WindowsError, PythonExceptions._WindowsError.ERROR_PATH_NOT_FOUND, "file does not exist: " + path));
  1102. }
  1103. long st_atime = (long)PythonTime.TicksToTimestamp(fi.LastAccessTime.ToUniversalTime().Ticks);
  1104. long st_ctime = (long)PythonTime.TicksToTimestamp(fi.CreationTime.ToUniversalTime().Ticks);
  1105. long st_mtime = (long)PythonTime.TicksToTimestamp(fi.LastWriteTime.ToUniversalTime().Ticks);
  1106. mode |= S_IREAD;
  1107. if ((fi.Attributes & FileAttributes.ReadOnly) == 0) {
  1108. mode |= S_IWRITE;
  1109. }
  1110. sr = new stat_result(mo

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